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
36 #include "dropletp.h"
37 #include <droplet/posix/posix.h>
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/stat.h>
41 #include <dirent.h>
42 #include <sys/types.h>
43 #include <linux/xattr.h>
44 #include <utime.h>
45 #include <pwd.h>
46 #include <grp.h>
47 #include <libgen.h>
48
49 /** @file */
50
51 //#define DPRINTF(fmt,...) fprintf(stderr, fmt, ##__VA_ARGS__)
52 #define DPRINTF(fmt, ...)
53
dpl_posix_map_errno()54 static dpl_status_t dpl_posix_map_errno()
55 {
56 switch (errno) {
57 case 0:
58 return DPL_SUCCESS;
59 case ENOENT:
60 return DPL_ENOENT;
61 case EINVAL:
62 return DPL_EINVAL;
63 case ENOMEM:
64 return DPL_ENOMEM;
65 case EIO:
66 return DPL_EIO;
67 case ENOTDIR:
68 return DPL_ENOTDIR;
69 case EEXIST:
70 return DPL_EEXIST;
71 case ENOTSUP:
72 return DPL_ENOTSUPP;
73 case EPERM:
74 return DPL_EPERM;
75 case ERANGE:
76 return DPL_ERANGEUNAVAIL;
77 default:
78 break;
79 }
80 return DPL_FAILURE;
81 }
82
dpl_posix_get_capabilities(dpl_ctx_t * ctx,dpl_capability_t * maskp)83 dpl_status_t dpl_posix_get_capabilities(dpl_ctx_t* ctx, dpl_capability_t* maskp)
84 {
85 if (NULL != maskp) *maskp = DPL_CAP_FNAMES;
86
87 return DPL_SUCCESS;
88 }
89
dpl_posix_head_raw(dpl_ctx_t * ctx,const char * bucket,const char * resource,const char * subresource,const dpl_option_t * option,dpl_ftype_t object_type,const dpl_condition_t * condition,dpl_dict_t ** metadatap,char ** locationp)90 dpl_status_t dpl_posix_head_raw(dpl_ctx_t* ctx,
91 const char* bucket,
92 const char* resource,
93 const char* subresource,
94 const dpl_option_t* option,
95 dpl_ftype_t object_type,
96 const dpl_condition_t* condition,
97 dpl_dict_t** metadatap,
98 char** locationp)
99 {
100 dpl_status_t ret, ret2;
101 char path[MAXPATHLEN];
102 int iret;
103 struct stat st;
104 char buf[256];
105 dpl_dict_t* metadata = NULL;
106 dpl_dict_t* subdict = NULL;
107 dpl_value_t value;
108
109 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "");
110
111 snprintf(path, sizeof(path), "/%s/%s", ctx->base_path ? ctx->base_path : "",
112 resource ? resource : "");
113
114 iret = stat(path, &st);
115 if (-1 == iret) {
116 ret = dpl_posix_map_errno();
117 goto end;
118 }
119
120 metadata = dpl_dict_new(13);
121 if (NULL == metadata) {
122 ret = DPL_ENOMEM;
123 goto end;
124 }
125
126 snprintf(buf, sizeof(buf), "%ld", st.st_dev);
127 ret2 = dpl_dict_add(metadata, "dev", buf, 0);
128 if (DPL_SUCCESS != ret2) {
129 ret = ret2;
130 goto end;
131 }
132
133 snprintf(buf, sizeof(buf), "%lX", st.st_ino);
134 ret2 = dpl_dict_add(metadata, "ino", buf, 0);
135 if (DPL_SUCCESS != ret2) {
136 ret = ret2;
137 goto end;
138 }
139
140 snprintf(buf, sizeof(buf), "%u", st.st_mode);
141 ret2 = dpl_dict_add(metadata, "mode", buf, 0);
142 if (DPL_SUCCESS != ret2) {
143 ret = ret2;
144 goto end;
145 }
146
147 snprintf(buf, sizeof(buf), "%ld", st.st_nlink);
148 ret2 = dpl_dict_add(metadata, "nlink", buf, 0);
149 if (DPL_SUCCESS != ret2) {
150 ret = ret2;
151 goto end;
152 }
153
154 snprintf(buf, sizeof(buf), "%u", st.st_uid);
155 ret2 = dpl_dict_add(metadata, "uid", buf, 0);
156 if (DPL_SUCCESS != ret2) {
157 ret = ret2;
158 goto end;
159 }
160
161 snprintf(buf, sizeof(buf), "%u", st.st_gid);
162 ret2 = dpl_dict_add(metadata, "gid", buf, 0);
163 if (DPL_SUCCESS != ret2) {
164 ret = ret2;
165 goto end;
166 }
167
168 snprintf(buf, sizeof(buf), "%lu", st.st_rdev);
169 ret2 = dpl_dict_add(metadata, "rdev", buf, 0);
170 if (DPL_SUCCESS != ret2) {
171 ret = ret2;
172 goto end;
173 }
174
175 snprintf(buf, sizeof(buf), "%lu", st.st_size);
176 ret2 = dpl_dict_add(metadata, "size", buf, 0);
177 if (DPL_SUCCESS != ret2) {
178 ret = ret2;
179 goto end;
180 }
181
182 snprintf(buf, sizeof(buf), "%lu", st.st_blksize);
183 ret2 = dpl_dict_add(metadata, "blksize", buf, 0);
184 if (DPL_SUCCESS != ret2) {
185 ret = ret2;
186 goto end;
187 }
188
189 snprintf(buf, sizeof(buf), "%lu", st.st_blocks);
190 ret2 = dpl_dict_add(metadata, "blocks", buf, 0);
191 if (DPL_SUCCESS != ret2) {
192 ret = ret2;
193 goto end;
194 }
195
196 snprintf(buf, sizeof(buf), "%lu", st.st_atime);
197 ret2 = dpl_dict_add(metadata, "atime", buf, 0);
198 if (DPL_SUCCESS != ret2) {
199 ret = ret2;
200 goto end;
201 }
202
203 snprintf(buf, sizeof(buf), "%lu", st.st_mtime);
204 ret2 = dpl_dict_add(metadata, "mtime", buf, 0);
205 if (DPL_SUCCESS != ret2) {
206 ret = ret2;
207 goto end;
208 }
209
210 snprintf(buf, sizeof(buf), "%lu", st.st_ctime);
211 ret2 = dpl_dict_add(metadata, "ctime", buf, 0);
212 if (DPL_SUCCESS != ret2) {
213 ret = ret2;
214 goto end;
215 }
216
217 subdict = dpl_dict_new(13);
218 if (NULL == subdict) {
219 ret = DPL_ENOMEM;
220 goto end;
221 }
222
223 ret2 = dpl_get_xattrs(path, subdict, DPL_POSIX_XATTR_PREFIX,
224 XATTRS_NO_ENCODING);
225 if (DPL_SUCCESS != ret2) {
226 ret = ret2;
227 goto end;
228 }
229
230 value.type = DPL_VALUE_SUBDICT;
231 value.subdict = subdict;
232 // dpl_dict_add_value dups the value, so don't prevent freeing subdict
233 ret2 = dpl_dict_add_value(metadata, "xattr", &value, 0);
234 if (DPL_SUCCESS != ret2) {
235 ret = ret2;
236 goto end;
237 }
238
239 if (NULL != metadatap) {
240 *metadatap = metadata;
241 metadata = NULL;
242 }
243
244 ret = DPL_SUCCESS;
245
246 end:
247
248 if (NULL != subdict) dpl_dict_free(subdict);
249
250 if (NULL != metadata) dpl_dict_free(metadata);
251
252 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ret=%d", ret);
253
254 return ret;
255 }
256
dpl_posix_head(dpl_ctx_t * ctx,const char * bucket,const char * resource,const char * subresource,const dpl_option_t * option,dpl_ftype_t object_type,const dpl_condition_t * condition,dpl_dict_t ** metadatap,dpl_sysmd_t * sysmdp,char ** locationp)257 dpl_status_t dpl_posix_head(dpl_ctx_t* ctx,
258 const char* bucket,
259 const char* resource,
260 const char* subresource,
261 const dpl_option_t* option,
262 dpl_ftype_t object_type,
263 const dpl_condition_t* condition,
264 dpl_dict_t** metadatap,
265 dpl_sysmd_t* sysmdp,
266 char** locationp)
267 {
268 dpl_status_t ret, ret2;
269 dpl_dict_t* all_mds = NULL;
270 char path[MAXPATHLEN];
271
272 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "");
273
274 snprintf(path, sizeof(path), "/%s/%s", ctx->base_path ? ctx->base_path : "",
275 resource ? resource : "");
276
277 ret2 = dpl_posix_head_raw(ctx, bucket, resource, subresource, option,
278 object_type, condition, &all_mds, locationp);
279 if (DPL_SUCCESS != ret2) {
280 ret = ret2;
281 goto end;
282 }
283
284 ret2 = dpl_posix_get_metadata_from_values(all_mds, metadatap, sysmdp);
285 if (DPL_SUCCESS != ret2) {
286 ret = ret2;
287 goto end;
288 }
289
290 ret = DPL_SUCCESS;
291
292 end:
293
294 if (NULL != all_mds) dpl_dict_free(all_mds);
295
296 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ret=%d", ret);
297
298 return ret;
299 }
300
dpl_posix_list_bucket(dpl_ctx_t * ctx,const char * bucket,const char * prefix,const char * delimiter,const int max_keys,dpl_vec_t ** objectsp,dpl_vec_t ** common_prefixesp,char ** locationp)301 dpl_status_t dpl_posix_list_bucket(dpl_ctx_t* ctx,
302 const char* bucket,
303 const char* prefix,
304 const char* delimiter,
305 const int max_keys,
306 dpl_vec_t** objectsp,
307 dpl_vec_t** common_prefixesp,
308 char** locationp)
309 {
310 DIR* dir = NULL;
311 dpl_status_t ret, ret2;
312 int iret;
313 char path[MAXPATHLEN];
314 char objpath[MAXPATHLEN];
315 struct dirent entry, *entryp;
316 struct stat st;
317 dpl_vec_t* common_prefixes = NULL;
318 dpl_vec_t* objects = NULL;
319 dpl_common_prefix_t* common_prefix = NULL;
320 dpl_object_t* object = NULL;
321 char buf[MAXPATHLEN];
322
323 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "");
324
325 if (strcmp(delimiter, "/")) {
326 ret = DPL_EINVAL;
327 goto end;
328 }
329
330 snprintf(path, sizeof(path), "/%s/%s", ctx->base_path ? ctx->base_path : "",
331 prefix ? prefix : "");
332
333 dir = opendir(path);
334 if (NULL == dir) {
335 ret = dpl_posix_map_errno();
336 perror("opendir");
337 goto end;
338 }
339
340 objects = dpl_vec_new(2, 2);
341 if (NULL == objects) {
342 ret = DPL_ENOMEM;
343 goto end;
344 }
345
346 common_prefixes = dpl_vec_new(2, 2);
347 if (NULL == common_prefixes) {
348 ret = DPL_ENOMEM;
349 goto end;
350 }
351
352 while (1) {
353 iret = readdir_r(dir, &entry, &entryp);
354 if (0 != iret) {
355 ret = dpl_posix_map_errno();
356 perror("readdir");
357 goto end;
358 }
359
360 if (!entryp) break;
361
362 if (!strcmp(entryp->d_name, ".") || !strcmp(entryp->d_name, "..")) continue;
363
364 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "%s", entryp->d_name);
365
366 if (entryp->d_type == DT_DIR) {
367 // this is a directory
368 snprintf(buf, sizeof(buf), "%s%s/", prefix ? prefix : "", entryp->d_name);
369 common_prefix = malloc(sizeof(*common_prefix));
370 if (NULL == common_prefix) {
371 ret = DPL_ENOMEM;
372 goto end;
373 }
374 memset(common_prefix, 0, sizeof(*common_prefix));
375 common_prefix->prefix = strdup(buf);
376 if (NULL == common_prefix->prefix) {
377 ret = DPL_ENOMEM;
378 goto end;
379 }
380
381 ret2 = dpl_vec_add(common_prefixes, common_prefix);
382 if (DPL_SUCCESS != ret2) {
383 ret = ret2;
384 goto end;
385 }
386
387 common_prefix = NULL;
388 } else {
389 snprintf(buf, sizeof(buf), "%s%s", prefix ? prefix : "", entryp->d_name);
390 object = malloc(sizeof(*object));
391 if (NULL == object) {
392 ret = DPL_ENOMEM;
393 goto end;
394 }
395 memset(object, 0, sizeof(*object));
396 object->path = strdup(buf);
397 if (NULL == object->path) {
398 ret = DPL_ENOMEM;
399 goto end;
400 }
401
402 snprintf(objpath, sizeof(objpath), "/%s/%s",
403 ctx->base_path ? ctx->base_path : "", object->path);
404 iret = stat(objpath, &st);
405 if (0 != iret) {
406 // It might be a broken link -> ENOENT, not an error, size=0
407 if (errno != ENOENT) {
408 // Do not map errno here, since it makes the whole listing fail,
409 // and we don't want to thwart the meaning of the error for the
410 // directory.
411 perror("stat");
412 ret = DPL_FAILURE;
413 goto end;
414 }
415 st.st_size = 0;
416 }
417 object->size = st.st_size;
418 object->last_modified = st.st_mtime;
419
420 switch (entryp->d_type) {
421 case DT_BLK:
422 object->type = DPL_FTYPE_BLKDEV;
423 break;
424 case DT_CHR:
425 object->type = DPL_FTYPE_CHRDEV;
426 break;
427 case DT_DIR:
428 object->type = DPL_FTYPE_DIR;
429 break;
430 case DT_FIFO:
431 object->type = DPL_FTYPE_FIFO;
432 break;
433 case DT_LNK:
434 object->type = DPL_FTYPE_SYMLINK;
435 break;
436 case DT_SOCK:
437 object->type = DPL_FTYPE_SOCKET;
438 break;
439 case DT_REG:
440 object->type = DPL_FTYPE_REG;
441 break;
442 case DT_UNKNOWN:
443 default:
444 object->type = DPL_FTYPE_UNDEF;
445 break;
446 }
447
448 ret2 = dpl_vec_add(objects, object);
449 if (DPL_SUCCESS != ret2) {
450 ret = ret2;
451 goto end;
452 }
453
454 object = NULL;
455 }
456 }
457
458 if (NULL != objectsp) {
459 *objectsp = objects;
460 objects = NULL; // consume it
461 }
462
463 if (NULL != common_prefixesp) {
464 *common_prefixesp = common_prefixes;
465 common_prefixes = NULL; // consume it
466 }
467
468 ret = DPL_SUCCESS;
469
470 end:
471
472 if (NULL != object) free(object);
473
474 if (NULL != common_prefix) free(common_prefix);
475
476 if (NULL != objects) dpl_vec_objects_free(objects);
477
478 if (NULL != common_prefixes) dpl_vec_common_prefixes_free(common_prefixes);
479
480 if (NULL != dir) closedir(dir);
481
482 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ret=%d", ret);
483
484 return ret;
485 }
486
487 /* WARNING, UNTESTED */
dpl_posix_list_bucket_attrs(dpl_ctx_t * ctx,const char * bucket,const char * prefix,const char * delimiter,const int max_keys,dpl_dict_t ** metadatap,dpl_sysmd_t * sysmdp,dpl_vec_t ** objectsp,dpl_vec_t ** common_prefixesp,char ** locationp)488 dpl_status_t dpl_posix_list_bucket_attrs(dpl_ctx_t* ctx,
489 const char* bucket,
490 const char* prefix,
491 const char* delimiter,
492 const int max_keys,
493 dpl_dict_t** metadatap,
494 dpl_sysmd_t* sysmdp,
495 dpl_vec_t** objectsp,
496 dpl_vec_t** common_prefixesp,
497 char** locationp)
498 {
499 dpl_status_t status;
500
501 status = dpl_posix_head(ctx, bucket, prefix, NULL, NULL, DPL_FTYPE_UNDEF,
502 NULL, metadatap, sysmdp, locationp);
503 if (DPL_SUCCESS != status) { goto end; }
504
505 status = dpl_posix_list_bucket(ctx, bucket, prefix, delimiter, max_keys,
506 objectsp, common_prefixesp, locationp);
507 if (DPL_SUCCESS != status) {
508 if (NULL != metadatap && NULL != *metadatap) {
509 dpl_dict_free(*metadatap);
510 *metadatap = NULL;
511 }
512 goto end;
513 }
514
515 end:
516 return status;
517 }
518
dpl_posix_put(dpl_ctx_t * ctx,const char * bucket,const char * resource,const char * subresource,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,const char * data_buf,unsigned int data_len,const dpl_dict_t * query_params,dpl_sysmd_t * returned_sysmdp,char ** locationp)519 dpl_status_t dpl_posix_put(dpl_ctx_t* ctx,
520 const char* bucket,
521 const char* resource,
522 const char* subresource,
523 const dpl_option_t* option,
524 dpl_ftype_t object_type,
525 const dpl_condition_t* condition,
526 const dpl_range_t* range,
527 const dpl_dict_t* metadata,
528 const dpl_sysmd_t* sysmd,
529 const char* data_buf,
530 unsigned int data_len,
531 const dpl_dict_t* query_params,
532 dpl_sysmd_t* returned_sysmdp,
533 char** locationp)
534 {
535 dpl_status_t ret, ret2;
536 int iret;
537 char path[MAXPATHLEN];
538 ssize_t cc;
539 int fd = -1;
540
541 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "");
542
543 snprintf(path, sizeof(path), "/%s/%s", ctx->base_path ? ctx->base_path : "",
544 resource ? resource : "");
545
546 switch (object_type) {
547 case DPL_FTYPE_UNDEF:
548 case DPL_FTYPE_ANY:
549 case DPL_FTYPE_CAP:
550 case DPL_FTYPE_DOM:
551 case DPL_FTYPE_CHRDEV:
552 case DPL_FTYPE_BLKDEV:
553 case DPL_FTYPE_FIFO:
554 case DPL_FTYPE_SOCKET:
555 case DPL_FTYPE_SYMLINK:
556 ret = DPL_EINVAL;
557 goto end;
558 case DPL_FTYPE_DIR:
559 iret = mkdir(path, 0700);
560 if (-1 == iret) {
561 if (ENOENT == errno) {
562 ret = DPL_ENOENT;
563 } else {
564 ret = dpl_posix_map_errno();
565 perror("mkdir");
566 }
567 goto end;
568 }
569 break;
570 case DPL_FTYPE_REG:
571 fd = creat(path, 0600);
572 if (-1 == fd) {
573 if (ENOENT == errno) {
574 ret = DPL_ENOENT;
575 } else {
576 ret = dpl_posix_map_errno();
577 perror("creat");
578 }
579 goto end;
580 }
581 break;
582 }
583
584 if (DPL_FTYPE_REG == object_type) {
585 uint64_t offset, length;
586
587 if (range) {
588 int range_len;
589
590 offset = range->start;
591 range_len = range->start - range->end;
592 if (data_len > range_len) {
593 ret = DPL_EINVAL;
594 goto end;
595 }
596
597 length = data_len;
598 } else {
599 offset = 0;
600 length = data_len;
601 }
602
603 iret = ftruncate(fd, offset + length);
604 if (-1 == iret) {
605 ret = dpl_posix_map_errno();
606 goto end;
607 }
608
609 cc = pwrite(fd, data_buf, length, offset);
610 if (-1 == cc) {
611 ret = dpl_posix_map_errno();
612 goto end;
613 }
614
615 if (data_len != cc) {
616 ret = DPL_FAILURE;
617 goto end;
618 }
619 }
620
621 ret2 = dpl_posix_setattr(path, metadata, sysmd);
622 if (DPL_SUCCESS != ret2) {
623 ret = ret2;
624 goto end;
625 }
626
627 ret = DPL_SUCCESS;
628
629 end:
630
631 if (-1 != fd) close(fd);
632
633 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ret=%d", ret);
634
635 return ret;
636 }
637
638
posix_readlink(dpl_ctx_t * ctx,char * path,const dpl_option_t * option,char ** locationp)639 static dpl_status_t posix_readlink(dpl_ctx_t* ctx,
640 char* path,
641 const dpl_option_t* option,
642 char** locationp)
643 {
644 dpl_status_t ret;
645 int iret;
646 struct stat st;
647 ssize_t cc;
648 uint64_t length;
649 char* target = NULL;
650
651 iret = lstat(path, &st);
652 if (-1 == iret) {
653 ret = dpl_posix_map_errno();
654 perror("lstat");
655 goto end;
656 }
657 // Add one byte for the nul byte for readlink.
658 length = st.st_size + 1;
659
660 target = malloc(length);
661 if (NULL == target) {
662 ret = DPL_ENOMEM;
663 goto end;
664 }
665
666 cc = readlink(path, target, length);
667 if (cc != st.st_size) {
668 ret = cc == -1 ? dpl_posix_map_errno() : DPL_FAILURE;
669 if (cc == -1) perror("readlink");
670 goto end;
671 }
672 target[cc] = 0;
673
674 if (locationp) {
675 *locationp = target;
676 target = NULL;
677 }
678
679 // "success" for a readlink
680 ret = DPL_EREDIRECT;
681
682 end:
683
684 if (NULL != target) free(target);
685
686 return ret;
687 }
688
posix_get(const char * path,const dpl_option_t * option,const dpl_range_t * range,char ** data_bufp,unsigned int * data_lenp,dpl_dict_t ** metadatap,dpl_sysmd_t * sysmdp)689 static dpl_status_t posix_get(const char* path,
690 const dpl_option_t* option,
691 const dpl_range_t* range,
692 char** data_bufp,
693 unsigned int* data_lenp,
694 dpl_dict_t** metadatap,
695 dpl_sysmd_t* sysmdp)
696 {
697 dpl_status_t ret;
698 int iret;
699 ssize_t cc;
700 int fd = -1;
701 uint64_t offset, length;
702 struct stat st;
703 u_int data_len;
704 char* data_buf = NULL;
705 int do_alloc = !(option && option->mask & DPL_OPTION_NOALLOC);
706
707 iret = stat(path, &st);
708 if (-1 == iret) {
709 ret = dpl_posix_map_errno();
710 perror("stat");
711 goto end;
712 }
713
714 data_len = st.st_size;
715
716 if (range) {
717 int range_len;
718
719 offset = range->start;
720 range_len = range->start - range->end;
721 if (data_len < range_len) {
722 ret = DPL_EINVAL;
723 goto end;
724 }
725
726 length = data_len;
727 } else {
728 offset = 0;
729 length = data_len;
730 }
731
732 if (!do_alloc) {
733 data_buf = *data_bufp;
734 length = *data_lenp;
735 } else {
736 data_buf = malloc(length);
737 if (NULL == data_buf) {
738 ret = DPL_ENOMEM;
739 goto end;
740 }
741 }
742
743 fd = open(path, O_RDONLY);
744 if (-1 == fd) {
745 ret = dpl_posix_map_errno();
746 perror("open");
747 goto end;
748 }
749
750 cc = pread(fd, data_buf, length, offset);
751 if (-1 == cc) {
752 ret = dpl_posix_map_errno();
753 goto end;
754 }
755
756 if (data_len != cc) {
757 ret = DPL_FAILURE;
758 goto end;
759 }
760
761 if (NULL != data_lenp) *data_lenp = length;
762
763 if (NULL != data_bufp) {
764 *data_bufp = data_buf;
765 data_buf = NULL;
766 }
767
768 ret = DPL_SUCCESS;
769
770 end:
771
772 if (do_alloc && NULL != data_buf) free(data_buf);
773
774 if (-1 != fd) close(fd);
775
776 return ret;
777 }
778
dpl_posix_get(dpl_ctx_t * ctx,const char * bucket,const char * resource,const char * subresource,const dpl_option_t * option,dpl_ftype_t object_type,const dpl_condition_t * condition,const dpl_range_t * range,char ** data_bufp,unsigned int * data_lenp,dpl_dict_t ** metadatap,dpl_sysmd_t * sysmdp,char ** locationp)779 dpl_status_t dpl_posix_get(dpl_ctx_t* ctx,
780 const char* bucket,
781 const char* resource,
782 const char* subresource,
783 const dpl_option_t* option,
784 dpl_ftype_t object_type,
785 const dpl_condition_t* condition,
786 const dpl_range_t* range,
787 char** data_bufp,
788 unsigned int* data_lenp,
789 dpl_dict_t** metadatap,
790 dpl_sysmd_t* sysmdp,
791 char** locationp)
792 {
793 dpl_status_t ret = DPL_FAILURE;
794 char path[MAXPATHLEN];
795
796 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "object_type=%i", object_type);
797
798 snprintf(path, sizeof(path), "%s/%s", ctx->base_path ? ctx->base_path : "",
799 resource ? resource : "");
800
801 switch (object_type) {
802 case DPL_FTYPE_UNDEF:
803 case DPL_FTYPE_CAP:
804 case DPL_FTYPE_DIR:
805 case DPL_FTYPE_DOM:
806 case DPL_FTYPE_CHRDEV:
807 case DPL_FTYPE_BLKDEV:
808 case DPL_FTYPE_FIFO:
809 case DPL_FTYPE_SOCKET:
810 ret = DPL_EINVAL;
811 goto end;
812 case DPL_FTYPE_SYMLINK:
813 ret = posix_readlink(ctx, path, option, locationp);
814 break;
815 case DPL_FTYPE_ANY:
816 case DPL_FTYPE_REG:
817 ret = posix_get(path, option, range, data_bufp, data_lenp, metadatap,
818 sysmdp);
819 break;
820 }
821
822 end:
823
824 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ret=%d", ret);
825
826 return ret;
827 }
828
dpl_posix_delete(dpl_ctx_t * ctx,const char * bucket,const char * resource,const char * subresource,const dpl_option_t * option,dpl_ftype_t object_type,const dpl_condition_t * condition,char ** locationp)829 dpl_status_t dpl_posix_delete(dpl_ctx_t* ctx,
830 const char* bucket,
831 const char* resource,
832 const char* subresource,
833 const dpl_option_t* option,
834 dpl_ftype_t object_type,
835 const dpl_condition_t* condition,
836 char** locationp)
837 {
838 dpl_status_t ret;
839 char path[MAXPATHLEN];
840 int iret;
841
842 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "");
843
844 snprintf(path, sizeof(path), "/%s/%s", ctx->base_path ? ctx->base_path : "",
845 resource ? resource : "");
846
847 switch (object_type) {
848 case DPL_FTYPE_UNDEF:
849 ret = DPL_ENOTSUPP;
850 goto end;
851 case DPL_FTYPE_ANY:
852 ret = DPL_ENOTSUPP;
853 goto end;
854 case DPL_FTYPE_REG:
855 iret = unlink(path);
856 if (-1 == iret) {
857 ret = dpl_posix_map_errno();
858 perror("unlink");
859 goto end;
860 }
861 ret = DPL_SUCCESS;
862 goto end;
863 case DPL_FTYPE_DIR:
864 iret = rmdir(path);
865 if (-1 == iret) {
866 if (ENOTEMPTY == errno) {
867 ret = DPL_ENOTEMPTY;
868 } else {
869 ret = dpl_posix_map_errno();
870 perror("rmdir");
871 }
872 goto end;
873 }
874 ret = DPL_SUCCESS;
875 goto end;
876 case DPL_FTYPE_CAP:
877 case DPL_FTYPE_DOM:
878 case DPL_FTYPE_CHRDEV:
879 case DPL_FTYPE_BLKDEV:
880 case DPL_FTYPE_FIFO:
881 case DPL_FTYPE_SOCKET:
882 case DPL_FTYPE_SYMLINK:
883 ret = DPL_ENOTSUPP;
884 goto end;
885 }
886
887 ret = DPL_SUCCESS;
888
889 end:
890
891 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ret=%d", ret);
892
893 return ret;
894 }
895
dpl_posix_copy(dpl_ctx_t * ctx,const char * src_bucket,const char * src_resource,const char * src_subresource,const char * dst_bucket,const char * dst_resource,const char * dst_subresource,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,char ** locationp)896 dpl_status_t dpl_posix_copy(dpl_ctx_t* ctx,
897 const char* src_bucket,
898 const char* src_resource,
899 const char* src_subresource,
900 const char* dst_bucket,
901 const char* dst_resource,
902 const char* dst_subresource,
903 const dpl_option_t* option,
904 dpl_ftype_t object_type,
905 dpl_copy_directive_t copy_directive,
906 const dpl_dict_t* metadata,
907 const dpl_sysmd_t* sysmd,
908 const dpl_condition_t* condition,
909 char** locationp)
910 {
911 dpl_status_t ret, ret2;
912 int iret;
913 char src_path[MAXPATHLEN];
914 char dst_path[MAXPATHLEN];
915
916 snprintf(src_path, sizeof(src_path), "/%s/%s",
917 ctx->base_path ? ctx->base_path : "",
918 src_resource ? src_resource : "");
919 snprintf(dst_path, sizeof(src_path), "/%s/%s",
920 ctx->base_path ? ctx->base_path : "",
921 dst_resource ? dst_resource : "");
922
923 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "directive: %s: %s -> %s",
924 dpl_copy_directive_to_str(copy_directive), src_path, dst_path);
925
926 switch (copy_directive) {
927 case DPL_COPY_DIRECTIVE_UNDEF:
928 break;
929 case DPL_COPY_DIRECTIVE_COPY:
930 ret = DPL_ENOTSUPP;
931 goto end;
932 case DPL_COPY_DIRECTIVE_METADATA_REPLACE:
933 if ((src_resource == NULL || dst_resource == NULL)
934 || strcmp(src_resource, dst_resource)) {
935 ret = DPL_EINVAL;
936 goto end;
937 }
938
939 ret2 = dpl_posix_setattr(src_path, metadata, sysmd);
940 if (DPL_SUCCESS != ret2) {
941 ret = ret2;
942 goto end;
943 }
944
945 break;
946 case DPL_COPY_DIRECTIVE_LINK:
947 iret = link(src_path, dst_path);
948 if (-1 == iret) {
949 ret = dpl_posix_map_errno();
950 perror("link");
951 goto end;
952 }
953 break;
954 case DPL_COPY_DIRECTIVE_SYMLINK:
955 iret = symlink(src_path, dst_path);
956 if (-1 == iret) {
957 ret = dpl_posix_map_errno();
958 perror("symlink");
959 goto end;
960 }
961 break;
962 case DPL_COPY_DIRECTIVE_MOVE:
963 iret = rename(src_path, dst_path);
964 if (-1 == iret) {
965 ret = dpl_posix_map_errno();
966 perror("rename");
967 goto end;
968 }
969 break;
970 case DPL_COPY_DIRECTIVE_MKDENT:
971 case DPL_COPY_DIRECTIVE_RMDENT:
972 case DPL_COPY_DIRECTIVE_MVDENT:
973 ret = DPL_ENOTSUPP;
974 goto end;
975 }
976
977 ret = DPL_SUCCESS;
978
979 end:
980
981 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ret=%d", ret);
982
983 return ret;
984 }
985
dpl_posix_stream_resume(dpl_ctx_t * ctx,dpl_stream_t * stream,struct json_object * status)986 dpl_status_t dpl_posix_stream_resume(dpl_ctx_t* ctx,
987 dpl_stream_t* stream,
988 struct json_object* status)
989 {
990 dpl_status_t ret = DPL_FAILURE;
991
992 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ctx=%p stream=%p status=%p", ctx, stream,
993 status);
994
995 if (NULL != stream->status) { json_object_put(stream->status); }
996
997 stream->status = json_object_get(status);
998
999 ret = DPL_SUCCESS;
1000
1001 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ret=%d", ret);
1002
1003 return ret;
1004 }
1005
dpl_posix_stream_getmd(dpl_ctx_t * ctx,dpl_stream_t * stream,dpl_dict_t ** metadatap,dpl_sysmd_t ** sysmdp)1006 dpl_status_t dpl_posix_stream_getmd(dpl_ctx_t* ctx,
1007 dpl_stream_t* stream,
1008 dpl_dict_t** metadatap,
1009 dpl_sysmd_t** sysmdp)
1010 {
1011 dpl_dict_t* all_mds = NULL;
1012 dpl_status_t ret = DPL_FAILURE;
1013
1014 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "");
1015
1016 ret = dpl_posix_head_raw(ctx, stream->bucket, stream->locator, NULL, NULL,
1017 DPL_FTYPE_REG, NULL, &all_mds, NULL);
1018 if (DPL_SUCCESS != ret) goto end;
1019
1020 ret = dpl_posix_get_metadata_from_values(all_mds, metadatap,
1021 sysmdp ? *sysmdp : NULL);
1022 if (DPL_SUCCESS != ret) goto end;
1023
1024 ret = DPL_SUCCESS;
1025 end:
1026
1027 if (all_mds) dpl_dict_free(all_mds);
1028
1029 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ret=%d", ret);
1030
1031 return ret;
1032 }
1033
dpl_posix_stream_get(dpl_ctx_t * ctx,dpl_stream_t * stream,unsigned int len,char ** bufp,unsigned int * lenp,struct json_object ** statusp)1034 dpl_status_t dpl_posix_stream_get(dpl_ctx_t* ctx,
1035 dpl_stream_t* stream,
1036 unsigned int len,
1037 char** bufp,
1038 unsigned int* lenp,
1039 struct json_object** statusp)
1040 {
1041 dpl_status_t ret = DPL_FAILURE;
1042 int iret;
1043 struct json_object* offset_object = NULL;
1044 unsigned int cur_off = 0;
1045 char path[MAXPATHLEN];
1046 char* buf = NULL;
1047 int fd = -1;
1048 int rd = 0;
1049
1050 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ctx=%p stream=%p len=%u", ctx, stream,
1051 len);
1052
1053 if (stream->locator_is_id) {
1054 ret = DPL_ENOTSUPP;
1055 goto end;
1056 }
1057
1058 iret = snprintf(path, sizeof(path), "/%s/%s",
1059 ctx->base_path ? ctx->base_path : "", stream->locator);
1060 if (iret > sizeof(path)) {
1061 ret = DPL_ENAMETOOLONG;
1062 goto end;
1063 }
1064
1065 buf = malloc(len);
1066 if (NULL == buf) {
1067 ret = DPL_ENOMEM;
1068 goto end;
1069 }
1070
1071 if (NULL == stream->status) {
1072 offset_object = json_object_new_int64(0);
1073 if (NULL == offset_object) {
1074 ret = DPL_ENOMEM;
1075 goto end;
1076 }
1077
1078 stream->status = json_object_new_object();
1079 if (NULL == stream->status) {
1080 json_object_put(offset_object);
1081 ret = DPL_ENOMEM;
1082 goto end;
1083 }
1084
1085 json_object_object_add(stream->status, "offset", offset_object);
1086 } else {
1087 if (json_object_object_get_ex(stream->status, "offset", &offset_object)
1088 == 0) {
1089 ret = DPL_FAILURE;
1090 goto end;
1091 }
1092 }
1093
1094 cur_off = json_object_get_int64(offset_object);
1095
1096 fd = open(path, O_RDONLY);
1097 if (-1 == fd) {
1098 ret = dpl_posix_map_errno();
1099 perror("open");
1100 goto end;
1101 }
1102 rd = pread(fd, buf, len, cur_off);
1103 if (rd < 0) {
1104 ret = dpl_posix_map_errno();
1105 perror("pread");
1106 goto end;
1107 }
1108
1109 offset_object = json_object_new_int64(cur_off + rd);
1110 if (NULL == offset_object) {
1111 ret = DPL_ENOMEM;
1112 goto end;
1113 }
1114 json_object_object_del(stream->status, "offset");
1115 json_object_object_add(stream->status, "offset", offset_object);
1116
1117 // Grab status object for the caller and return it.
1118 if (statusp) {
1119 *statusp = stream->status;
1120 json_object_get(*statusp);
1121 }
1122 if (lenp) *lenp = rd;
1123 if (bufp) {
1124 *bufp = buf;
1125 buf = NULL;
1126 }
1127
1128 ret = DPL_SUCCESS;
1129
1130 end:
1131 if (-1 != fd) close(fd);
1132 if (NULL != buf) free(buf);
1133
1134 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ret=%d", ret);
1135
1136 return ret;
1137 }
1138
dpl_posix_stream_putmd(dpl_ctx_t * ctx,dpl_stream_t * stream,dpl_dict_t * metadata,dpl_sysmd_t * sysmd)1139 dpl_status_t dpl_posix_stream_putmd(dpl_ctx_t* ctx,
1140 dpl_stream_t* stream,
1141 dpl_dict_t* metadata,
1142 dpl_sysmd_t* sysmd)
1143 {
1144 dpl_status_t ret = DPL_FAILURE;
1145 char path[MAXPATHLEN];
1146
1147 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "");
1148
1149 snprintf(path, sizeof(path), "/%s/%s", ctx->base_path ? ctx->base_path : "",
1150 stream->locator);
1151
1152 ret = dpl_posix_setattr(path, metadata, sysmd);
1153 if (DPL_SUCCESS != ret) goto end;
1154
1155 ret = DPL_SUCCESS;
1156 end:
1157
1158 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ret=%d", ret);
1159
1160 return ret;
1161 }
1162
dpl_posix_stream_put(dpl_ctx_t * ctx,dpl_stream_t * stream,char * buf,unsigned int len,struct json_object ** statusp)1163 dpl_status_t dpl_posix_stream_put(dpl_ctx_t* ctx,
1164 dpl_stream_t* stream,
1165 char* buf,
1166 unsigned int len,
1167 struct json_object** statusp)
1168 {
1169 dpl_status_t ret = DPL_FAILURE;
1170 int iret;
1171 struct json_object* offset_object = NULL;
1172 unsigned int cur_off = 0;
1173 char path[MAXPATHLEN];
1174 int fd = -1;
1175 int written = 0;
1176
1177 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ctx=%p stream=%p buf=%p len=%u", ctx,
1178 stream, buf, len);
1179
1180 if (stream->locator_is_id) {
1181 ret = DPL_ENOTSUPP;
1182 goto end;
1183 }
1184
1185 iret = snprintf(path, sizeof(path), "/%s/%s",
1186 ctx->base_path ? ctx->base_path : "", stream->locator);
1187 if (iret > sizeof(path)) {
1188 ret = DPL_ENAMETOOLONG;
1189 goto end;
1190 }
1191
1192 if (NULL == stream->status) {
1193 offset_object = json_object_new_int64(0);
1194 if (NULL == offset_object) {
1195 ret = DPL_ENOMEM;
1196 goto end;
1197 }
1198
1199 stream->status = json_object_new_object();
1200 if (NULL == stream->status) {
1201 json_object_put(offset_object);
1202 ret = DPL_ENOMEM;
1203 goto end;
1204 }
1205
1206 json_object_object_add(stream->status, "offset", offset_object);
1207 } else {
1208 if (json_object_object_get_ex(stream->status, "offset", &offset_object)
1209 == 0) {
1210 ret = DPL_FAILURE;
1211 goto end;
1212 }
1213 }
1214
1215 cur_off = json_object_get_int64(offset_object);
1216
1217 fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
1218 if (-1 == fd) {
1219 ret = dpl_posix_map_errno();
1220 perror("open");
1221 goto end;
1222 }
1223 written = pwrite(fd, buf, len, cur_off);
1224 if (written < len) {
1225 ret = dpl_posix_map_errno();
1226 perror("pwrite");
1227 goto end;
1228 }
1229
1230 offset_object = json_object_new_int64(cur_off + written);
1231 if (NULL == offset_object) {
1232 ret = DPL_ENOMEM;
1233 goto end;
1234 }
1235 json_object_object_del(stream->status, "offset");
1236 json_object_object_add(stream->status, "offset", offset_object);
1237
1238 // Grab status object for the caller and return it.
1239 if (statusp) {
1240 *statusp = stream->status;
1241 json_object_get(*statusp);
1242 }
1243
1244 ret = DPL_SUCCESS;
1245
1246 end:
1247 if (-1 != fd) close(fd);
1248
1249 DPL_TRACE(ctx, DPL_TRACE_BACKEND, "ret=%d", ret);
1250
1251 return ret;
1252 }
1253
dpl_posix_stream_flush(dpl_ctx_t * ctx,dpl_stream_t * stream)1254 dpl_status_t dpl_posix_stream_flush(dpl_ctx_t* ctx, dpl_stream_t* stream)
1255 {
1256 return DPL_SUCCESS;
1257 }
1258
1259 dpl_backend_t dpl_backend_posix = {
1260 "posix",
1261 .get_capabilities = dpl_posix_get_capabilities,
1262 .list_bucket = dpl_posix_list_bucket,
1263 .list_bucket_attrs = dpl_posix_list_bucket_attrs, /* WARNING, UNTESTED */
1264 .put = dpl_posix_put,
1265 .get = dpl_posix_get,
1266 .head = dpl_posix_head,
1267 .head_raw = dpl_posix_head_raw,
1268 .deletef = dpl_posix_delete,
1269 .copy = dpl_posix_copy,
1270 .stream_resume = dpl_posix_stream_resume,
1271 .stream_getmd = dpl_posix_stream_getmd,
1272 .stream_get = dpl_posix_stream_get,
1273 .stream_putmd = dpl_posix_stream_putmd,
1274 .stream_put = dpl_posix_stream_put,
1275 .stream_flush = dpl_posix_stream_flush,
1276 };
1277