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