IOR
aiori-S3-libs3.c
Go to the documentation of this file.
1 /*
2 * S3 implementation using the newer libs3
3 * https://github.com/bji/libs3
4 * Use one object per file chunk
5 */
6 
7 #ifdef HAVE_CONFIG_H
8 # include "config.h"
9 #endif
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <time.h>
15 
16 #include <libs3.h>
17 
18 #include "ior.h"
19 #include "aiori.h"
20 #include "aiori-debug.h"
21 #include "utilities.h"
22 
23 
25 
26 static void s3_xfer_hints(aiori_xfer_hint_t * params){
27  hints = params;
28 }
29 
30 /************************** O P T I O N S *****************************/
31 typedef struct {
33  char * access_key;
34  char * secret_key;
35  char * host;
36  char * bucket_prefix;
39  char * authRegion;
40 
41  int timeout;
44  int use_ssl;
45  S3BucketContext bucket_context;
46  S3Protocol s3_protocol;
47 } s3_options_t;
48 
49 static option_help * S3_options(aiori_mod_opt_t ** init_backend_options, aiori_mod_opt_t * init_values){
50  s3_options_t * o = malloc(sizeof(s3_options_t));
51  if (init_values != NULL){
52  memcpy(o, init_values, sizeof(s3_options_t));
53  }else{
54  memset(o, 0, sizeof(s3_options_t));
55  }
56 
57  *init_backend_options = (aiori_mod_opt_t*) o;
58  o->bucket_prefix = "ior";
59  o->bucket_prefix_cur = "b";
60 
61  option_help h [] = {
62  {0, "S3-libs3.bucket-per-file", "Use one bucket to map one file/directory, otherwise one bucket is used to store all dirs/files.", OPTION_FLAG, 'd', & o->bucket_per_file},
63  {0, "S3-libs3.bucket-name-prefix", "The prefix of the bucket(s).", OPTION_OPTIONAL_ARGUMENT, 's', & o->bucket_prefix},
64  {0, "S3-libs3.dont-suffix-bucket", "By default a hash will be added to the bucket name to increase uniqueness, this disables the option.", OPTION_FLAG, 'd', & o->dont_suffix },
65  {0, "S3-libs3.s3-compatible", "to be selected when using S3 compatible storage", OPTION_FLAG, 'd', & o->s3_compatible },
66  {0, "S3-libs3.use-ssl", "used to specify that SSL is needed for the connection", OPTION_FLAG, 'd', & o->use_ssl },
67  {0, "S3-libs3.host", "The host optionally followed by:port.", OPTION_OPTIONAL_ARGUMENT, 's', & o->host},
68  {0, "S3-libs3.secret-key", "The secret key.", OPTION_OPTIONAL_ARGUMENT, 's', & o->secret_key},
69  {0, "S3-libs3.access-key", "The access key.", OPTION_OPTIONAL_ARGUMENT, 's', & o->access_key},
70  {0, "S3-libs3.region", "The region used for the authorization signature.", OPTION_OPTIONAL_ARGUMENT, 's', & o->authRegion},
71  {0, "S3-libs3.location", "The bucket geographic location.", OPTION_OPTIONAL_ARGUMENT, 's', & o->locationConstraint},
73  };
74  option_help * help = malloc(sizeof(h));
75  memcpy(help, h, sizeof(h));
76  return help;
77 }
78 
79 static void def_file_name(s3_options_t * o, char * out_name, char const * path){
80  if(o->bucket_per_file){
81  out_name += sprintf(out_name, "%s-", o->bucket_prefix_cur);
82  }
83  // duplicate path except "/"
84  while(*path != 0){
85  char c = *path;
86  if(((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') )){
87  *out_name = *path;
88  out_name++;
89  }else if(c >= 'A' && c <= 'Z'){
90  *out_name = *path + ('a' - 'A');
91  out_name++;
92  }else if(c == '/'){
93  *out_name = '_';
94  out_name++;
95  }
96  path++;
97  }
98  *out_name = 'b';
99  out_name++;
100  *out_name = '\0';
101 }
102 
103 static void def_bucket_name(s3_options_t * o, char * out_name, char const * path){
104  // S3_MAX_BUCKET_NAME_SIZE
105  if(o->bucket_per_file){
106  out_name += sprintf(out_name, "%s-", o->bucket_prefix_cur);
107  }
108  // duplicate path except "/"
109  while(*path != 0){
110  char c = *path;
111  if(((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') )){
112  *out_name = *path;
113  out_name++;
114  }else if(c >= 'A' && c <= 'Z'){
115  *out_name = *path + ('a' - 'A');
116  out_name++;
117  }
118  path++;
119  }
120  *out_name = '\0';
121 
122  // S3Status S3_validate_bucket_name(const char *bucketName, S3UriStyle uriStyle);
123 }
124 
127  int64_t size;
128 };
129 
130 static S3Status s3status = S3StatusInterrupted;
131 static S3ErrorDetails s3error = {NULL};
132 
133 static S3Status responsePropertiesCallback(const S3ResponseProperties *properties, void *callbackData){
134  s3status = S3StatusOK;
135  return s3status;
136 }
137 
138 static void responseCompleteCallback(S3Status status, const S3ErrorDetails *error, void *callbackData) {
139  s3status = status;
140  if (error == NULL){
141  s3error.message = NULL;
142  }else{
143  s3error = *error;
144  }
145  return;
146 }
147 
148 #define CHECK_ERROR(p) \
149 if (s3status != S3StatusOK){ \
150  EWARNF("S3 %s:%d (path:%s) \"%s\": %s %s", __FUNCTION__, __LINE__, p, S3_get_status_name(s3status), s3error.message, s3error.furtherDetails ? s3error.furtherDetails : ""); \
151 }
152 
153 
155 
156 static char * S3_getVersion()
157 {
158  return "0.5";
159 }
160 
162 {
163  // Not needed
164 }
165 
166 
168 {
169  // Not needed
170 }
171 
172 static S3Status S3ListResponseCallback(const char *ownerId, const char *ownerDisplayName, const char *bucketName, int64_t creationDateSeconds, void *callbackData){
173  uint64_t * count = (uint64_t*) callbackData;
174  *count++;
175  return S3StatusOK;
176 }
177 
179 
180 static int S3_statfs (const char * path, ior_aiori_statfs_t * stat, aiori_mod_opt_t * options){
181  stat->f_bsize = 1;
182  stat->f_blocks = 1;
183  stat->f_bfree = 1;
184  stat->f_bavail = 1;
185  stat->f_ffree = 1;
186  s3_options_t * o = (s3_options_t*) options;
187 
188  // use the number of bucket as files
189  uint64_t buckets = 0;
190  S3_list_service(o->s3_protocol, o->access_key, o->secret_key, NULL, o->host,
191  o->authRegion, NULL, o->timeout, & listhandler, & buckets);
192  stat->f_files = buckets;
194 
195  return 0;
196 }
197 
198 static S3Status S3multipart_handler(const char *upload_id, void *callbackData){
199  *((char const**)(callbackData)) = upload_id;
200  return S3StatusOK;
201 }
202 
204 
205 typedef struct{
206  char * object;
207 } S3_fd_t;
208 
209 static int putObjectDataCallback(int bufferSize, char *buffer, void *callbackData){
210  struct data_handling * dh = (struct data_handling *) callbackData;
211  const int64_t size = dh->size > bufferSize ? bufferSize : dh->size;
212  if(size == 0) return 0;
213  memcpy(buffer, dh->buf, size);
214  dh->buf = (IOR_size_t*) ((char*)(dh->buf) + size);
215  dh->size -= size;
216 
217  return size;
218 }
219 
221 
222 static aiori_fd_t *S3_Create(char *path, int iorflags, aiori_mod_opt_t * options)
223 {
224  char * upload_id;
225  s3_options_t * o = (s3_options_t*) options;
226  char p[FILENAME_MAX];
227  def_file_name(o, p, path);
228 
229 
230  if(iorflags & IOR_CREAT){
231  if(o->bucket_per_file){
232  S3_create_bucket(o->s3_protocol, o->access_key, o->secret_key, NULL, o->host, p, o->authRegion, S3CannedAclPrivate, o->locationConstraint, NULL, o->timeout, & responseHandler, NULL);
233  }else{
234  struct data_handling dh = { .buf = NULL, .size = 0 };
235  S3_put_object(& o->bucket_context, p, 0, NULL, NULL, o->timeout, &putObjectHandler, & dh);
236  }
237  if (s3status != S3StatusOK){
238  CHECK_ERROR(p);
239  return NULL;
240  }
241  }
242 
243  S3_fd_t * fd = malloc(sizeof(S3_fd_t));
244  fd->object = strdup(p);
245  return (aiori_fd_t*) fd;
246 }
247 
248 
249 static S3Status statResponsePropertiesCallback(const S3ResponseProperties *properties, void *callbackData){
250  // check the size
251  struct stat *buf = (struct stat*) callbackData;
252  if(buf != NULL){
253  buf->st_size = properties->contentLength;
254  buf->st_mtime = properties->lastModified;
255  }
256  s3status = S3StatusOK;
257  return s3status;
258 }
259 
261 
262 static aiori_fd_t *S3_Open(char *path, int flags, aiori_mod_opt_t * options)
263 {
264  if(flags & IOR_CREAT){
265  return S3_Create(path, flags, options);
266  }
267  if(flags & IOR_WRONLY){
268  WARN("S3 IOR_WRONLY is not supported");
269  }
270  if(flags & IOR_RDWR){
271  WARN("S3 IOR_RDWR is not supported");
272  }
273 
274  s3_options_t * o = (s3_options_t*) options;
275  char p[FILENAME_MAX];
276  def_file_name(o, p, path);
277 
278  if (o->bucket_per_file){
279  S3_test_bucket(o->s3_protocol, S3UriStylePath, o->access_key, o->secret_key,
280  NULL, o->host, p, o->authRegion, 0, NULL,
281  NULL, o->timeout, & responseHandler, NULL);
282  }else{
283  struct stat buf;
284  S3_head_object(& o->bucket_context, p, NULL, o->timeout, & statResponseHandler, & buf);
285  }
286  if (s3status != S3StatusOK){
287  CHECK_ERROR(p);
288  return NULL;
289  }
290 
291  S3_fd_t * fd = malloc(sizeof(S3_fd_t));
292  fd->object = strdup(p);
293  return (aiori_fd_t*) fd;
294 }
295 
296 static S3Status getObjectDataCallback(int bufferSize, const char *buffer, void *callbackData){
297  struct data_handling * dh = (struct data_handling *) callbackData;
298  const int64_t size = dh->size > bufferSize ? bufferSize : dh->size;
299  memcpy(dh->buf, buffer, size);
300  dh->buf = (IOR_size_t*) ((char*)(dh->buf) + size);
301  dh->size -= size;
302 
303  return S3StatusOK;
304 }
305 
307 
309  S3_fd_t * fd = (S3_fd_t *) afd;
310  struct data_handling dh = { .buf = buffer, .size = length };
311 
312  s3_options_t * o = (s3_options_t*) options;
313  char p[FILENAME_MAX];
314 
315  if(o->bucket_per_file){
316  o->bucket_context.bucketName = fd->object;
317  if(offset != 0){
318  sprintf(p, "%ld-%ld", (long) offset, (long) length);
319  }else{
320  sprintf(p, "0");
321  }
322  }else{
323  if(offset != 0){
324  sprintf(p, "%s-%ld-%ld", fd->object, (long) offset, (long) length);
325  }else{
326  sprintf(p, "%s", fd->object);
327  }
328  }
329  if(access == WRITE){
330  S3_put_object(& o->bucket_context, p, length, NULL, NULL, o->timeout, &putObjectHandler, & dh);
331  }else{
332  S3_get_object(& o->bucket_context, p, NULL, 0, length, NULL, o->timeout, &getObjectHandler, & dh);
333  }
334  if (! o->s3_compatible){
335  CHECK_ERROR(p);
336  }
337  return length;
338 }
339 
340 
342 {
343  S3_fd_t * fd = (S3_fd_t *) afd;
344  free(fd->object);
345  free(afd);
346 }
347 
348 typedef struct {
349  int status; // do not reorder!
352  char const *nextMarker;
353 } s3_delete_req;
354 
355 S3Status list_delete_cb(int isTruncated, const char *nextMarker, int contentsCount, const S3ListBucketContent *contents, int commonPrefixesCount, const char **commonPrefixes, void *callbackData){
356  s3_delete_req * req = (s3_delete_req*) callbackData;
357  for(int i=0; i < contentsCount; i++){
358  S3_delete_object(& req->o->bucket_context, contents[i].key, NULL, req->o->timeout, & responseHandler, NULL);
359  }
360  req->truncated = isTruncated;
361  if(isTruncated){
362  req->nextMarker = nextMarker;
363  }
364  return S3StatusOK;
365 }
366 
368 
369 static void S3_Delete(char *path, aiori_mod_opt_t * options)
370 {
371  s3_options_t * o = (s3_options_t*) options;
372  char p[FILENAME_MAX];
373  def_file_name(o, p, path);
374 
375 
376  if(o->bucket_per_file){
377  o->bucket_context.bucketName = p;
378  s3_delete_req req = {0, o, 0, NULL};
379  do{
380  S3_list_bucket(& o->bucket_context, NULL, req.nextMarker, NULL, INT_MAX, NULL, o->timeout, & list_delete_handler, & req);
381  }while(req.truncated);
382  S3_delete_bucket(o->s3_protocol, S3UriStylePath, o->access_key, o->secret_key, NULL, o->host, p, o->authRegion, NULL, o->timeout, & responseHandler, NULL);
383  }else{
384  s3_delete_req req = {0, o, 0, NULL};
385  do{
386  S3_list_bucket(& o->bucket_context, p, req.nextMarker, NULL, INT_MAX, NULL, o->timeout, & list_delete_handler, & req);
387  }while(req.truncated);
388  S3_delete_object(& o->bucket_context, p, NULL, o->timeout, & responseHandler, NULL);
389  }
390  CHECK_ERROR(p);
391 }
392 
393 static int S3_mkdir (const char *path, mode_t mode, aiori_mod_opt_t * options){
394  s3_options_t * o = (s3_options_t*) options;
395  char p[FILENAME_MAX];
396  def_bucket_name(o, p, path);
397 
398 
399  if (o->bucket_per_file){
400  S3_create_bucket(o->s3_protocol, o->access_key, o->secret_key, NULL, o->host, p, o->authRegion, S3CannedAclPrivate, o->locationConstraint, NULL, o->timeout, & responseHandler, NULL);
401  CHECK_ERROR(p);
402  return 0;
403  }else{
404  struct data_handling dh = { .buf = NULL, .size = 0 };
405  S3_put_object(& o->bucket_context, p, 0, NULL, NULL, o->timeout, & putObjectHandler, & dh);
406  if (! o->s3_compatible){
407  CHECK_ERROR(p);
408  }
409  return 0;
410  }
411 }
412 
413 static int S3_rmdir (const char *path, aiori_mod_opt_t * options){
414  s3_options_t * o = (s3_options_t*) options;
415  char p[FILENAME_MAX];
416 
417  def_bucket_name(o, p, path);
418  if (o->bucket_per_file){
419  S3_delete_bucket(o->s3_protocol, S3UriStylePath, o->access_key, o->secret_key, NULL, o->host, p, o->authRegion, NULL, o->timeout, & responseHandler, NULL);
420  CHECK_ERROR(p);
421  return 0;
422  }else{
423  S3_delete_object(& o->bucket_context, p, NULL, o->timeout, & responseHandler, NULL);
424  CHECK_ERROR(p);
425  return 0;
426  }
427 }
428 
429 static int S3_stat(const char *path, struct stat *buf, aiori_mod_opt_t * options){
430  s3_options_t * o = (s3_options_t*) options;
431  char p[FILENAME_MAX];
432  def_file_name(o, p, path);
433  memset(buf, 0, sizeof(struct stat));
434  // TODO count the individual file fragment sizes together
435  if (o->bucket_per_file){
436  S3_test_bucket(o->s3_protocol, S3UriStylePath, o->access_key, o->secret_key,
437  NULL, o->host, p, o->authRegion, 0, NULL,
438  NULL, o->timeout, & responseHandler, NULL);
439  }else{
440  S3_head_object(& o->bucket_context, p, NULL, o->timeout, & statResponseHandler, buf);
441  }
442  if (s3status != S3StatusOK){
443  return -1;
444  }
445  return 0;
446 }
447 
448 static int S3_access (const char *path, int mode, aiori_mod_opt_t * options){
449  struct stat buf;
450  return S3_stat(path, & buf, options);
451 }
452 
453 static IOR_offset_t S3_GetFileSize(aiori_mod_opt_t * options, char *testFileName)
454 {
455  struct stat buf;
456  if(S3_stat(testFileName, & buf, options) != 0) return -1;
457  return buf.st_size;
458 }
459 
460 
462  s3_options_t * o = (s3_options_t*) options;
463  if(o->access_key == NULL){
464  o->access_key = "";
465  }
466  if(o->secret_key == NULL){
467  o->secret_key = "";
468  }
469  if(o->host == NULL){
470  WARN("The S3 hostname should be specified");
471  }
472  return 0;
473 }
474 
476  s3_options_t * o = (s3_options_t*) options;
477  int ret = S3_initialize(NULL, S3_INIT_ALL, o->host);
478  if(ret != S3StatusOK)
479  FAIL("Could not initialize S3 library");
480 
481  // create a bucket id based on access-key using a trivial checksumming
482  if(! o->dont_suffix){
483  uint64_t c = 0;
484  char * r = o->access_key;
485  for(uint64_t pos = 1; (*r) != '\0' ; r++, pos*=10) {
486  c += (*r) * pos;
487  }
488  int count = snprintf(NULL, 0, "%s%lu", o->bucket_prefix, c % 1000);
489  char * old_prefix = o->bucket_prefix;
490  o->bucket_prefix_cur = malloc(count + 1);
491  sprintf(o->bucket_prefix_cur, "%s%lu", old_prefix, c % 1000);
492  }else{
494  }
495 
496  // init bucket context
497  memset(& o->bucket_context, 0, sizeof(o->bucket_context));
498  o->bucket_context.hostName = o->host;
499  o->bucket_context.bucketName = o->bucket_prefix_cur;
500  if (o->use_ssl){
501  o->s3_protocol = S3ProtocolHTTPS;
502  }else{
503  o->s3_protocol = S3ProtocolHTTP;
504  }
505  o->bucket_context.protocol = o->s3_protocol;
506  o->bucket_context.uriStyle = S3UriStylePath;
507  o->bucket_context.accessKeyId = o->access_key;
508  o->bucket_context.secretAccessKey = o->secret_key;
509 
510  if (! o->bucket_per_file && rank == 0){
511  S3_create_bucket(o->s3_protocol, o->access_key, o->secret_key, NULL, o->host, o->bucket_context.bucketName, o->authRegion, S3CannedAclPrivate, o->locationConstraint, NULL, o->timeout, & responseHandler, NULL);
512  CHECK_ERROR(o->bucket_context.bucketName);
513  }
514 
515  if ( ret != S3StatusOK ){
516  FAIL("S3 error %s", S3_get_status_name(ret));
517  }
518 }
519 
521  s3_options_t * o = (s3_options_t*) options;
522  if (! o->bucket_per_file && rank == 0){
523  S3_delete_bucket(o->s3_protocol, S3UriStylePath, o->access_key, o->secret_key, NULL, o->host, o->bucket_context.bucketName, o->authRegion, NULL, o->timeout, & responseHandler, NULL);
524  CHECK_ERROR(o->bucket_context.bucketName);
525  }
526 
527  S3_deinitialize();
528 }
529 
530 
532  .name = "S3-libs3",
533  .name_legacy = NULL,
534  .create = S3_Create,
535  .open = S3_Open,
536  .xfer = S3_Xfer,
537  .close = S3_Close,
538  .delete = S3_Delete,
539  .get_version = S3_getVersion,
540  .fsync = S3_Fsync,
541  .xfer_hints = s3_xfer_hints,
542  .get_file_size = S3_GetFileSize,
543  .statfs = S3_statfs,
544  .mkdir = S3_mkdir,
545  .rmdir = S3_rmdir,
546  .access = S3_access,
547  .stat = S3_stat,
548  .initialize = S3_init,
549  .finalize = S3_final,
550  .get_options = S3_options,
551  .check_params = S3_check_params,
552  .sync = S3_Sync,
553  .enable_mdtest = true
554 };
char * access_key
uint64_t f_blocks
Definition: aiori.h:53
static S3MultipartInitialHandler multipart_handler
static int putObjectDataCallback(int bufferSize, char *buffer, void *callbackData)
static IOR_offset_t S3_Xfer(int access, aiori_fd_t *afd, IOR_size_t *buffer, IOR_offset_t length, IOR_offset_t offset, aiori_mod_opt_t *options)
uint64_t f_bfree
Definition: aiori.h:54
#define LAST_OPTION
Definition: option.h:39
char const * nextMarker
static S3Status s3status
static void S3_Close(aiori_fd_t *afd, aiori_mod_opt_t *options)
IOR_size_t * buf
S3Status list_delete_cb(int isTruncated, const char *nextMarker, int contentsCount, const S3ListBucketContent *contents, int commonPrefixesCount, const char **commonPrefixes, void *callbackData)
struct benchmark_options o
Definition: md-workbench.c:128
static S3ErrorDetails s3error
S3BucketContext bucket_context
static aiori_xfer_hint_t * hints
static int S3_check_params(aiori_mod_opt_t *options)
static S3Status statResponsePropertiesCallback(const S3ResponseProperties *properties, void *callbackData)
static S3GetObjectHandler getObjectHandler
static void S3_Fsync(aiori_fd_t *fd, aiori_mod_opt_t *options)
static aiori_fd_t * S3_Open(char *path, int flags, aiori_mod_opt_t *options)
static char * S3_getVersion()
uint64_t f_ffree
Definition: aiori.h:57
static aiori_fd_t * S3_Create(char *path, int iorflags, aiori_mod_opt_t *options)
static void def_file_name(s3_options_t *o, char *out_name, char const *path)
#define FAIL(...)
Definition: aiori-debug.h:12
static void S3_Sync(aiori_mod_opt_t *options)
#define WRITE
Definition: iordef.h:86
char * locationConstraint
char * bucket_prefix
static S3Status S3ListResponseCallback(const char *ownerId, const char *ownerDisplayName, const char *bucketName, int64_t creationDateSeconds, void *callbackData)
static S3Status responsePropertiesCallback(const S3ResponseProperties *properties, void *callbackData)
#define IOR_CREAT
Definition: aiori.h:32
static void def_bucket_name(s3_options_t *o, char *out_name, char const *path)
static S3ListServiceHandler listhandler
uint64_t f_files
Definition: aiori.h:56
static option_help options[]
Definition: aiori-CEPHFS.c:54
static option_help * S3_options(aiori_mod_opt_t **init_backend_options, aiori_mod_opt_t *init_values)
#define CHECK_ERROR(p)
uint64_t f_bsize
Definition: aiori.h:52
static S3ListBucketHandler list_delete_handler
static void S3_final(aiori_mod_opt_t *options)
static S3ResponseHandler statResponseHandler
char * bucket_prefix_cur
#define WARN(MSG)
Definition: aiori-debug.h:32
static S3Status S3multipart_handler(const char *upload_id, void *callbackData)
S3Protocol s3_protocol
static int S3_stat(const char *path, struct stat *buf, aiori_mod_opt_t *options)
static IOR_offset_t S3_GetFileSize(aiori_mod_opt_t *options, char *testFileName)
Definition: ior.h:56
char * secret_key
static S3Status getObjectDataCallback(int bufferSize, const char *buffer, void *callbackData)
char * object
static int S3_mkdir(const char *path, mode_t mode, aiori_mod_opt_t *options)
#define IOR_WRONLY
Definition: aiori.h:29
long long int IOR_size_t
Definition: iordef.h:110
static void S3_Delete(char *path, aiori_mod_opt_t *options)
uint64_t f_bavail
Definition: aiori.h:55
static int S3_rmdir(const char *path, aiori_mod_opt_t *options)
static S3PutObjectHandler putObjectHandler
static void S3_init(aiori_mod_opt_t *options)
static void responseCompleteCallback(S3Status status, const S3ErrorDetails *error, void *callbackData)
char * authRegion
#define IOR_RDWR
Definition: aiori.h:30
static void s3_xfer_hints(aiori_xfer_hint_t *params)
char * name
Definition: aiori.h:88
static int S3_access(const char *path, int mode, aiori_mod_opt_t *options)
long long int IOR_offset_t
Definition: iordef.h:109
int rank
Definition: utilities.c:68
ior_aiori_t S3_libS3_aiori
s3_options_t * o
static int S3_statfs(const char *path, ior_aiori_statfs_t *stat, aiori_mod_opt_t *options)
static S3ResponseHandler responseHandler
#define NULL
Definition: iordef.h:70