Coverage Report

Created: 2026-11-12 07:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fluent-bit/plugins/out_s3/s3_store.c
Line
Count
Source
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*  Fluent Bit
4
 *  ==========
5
 *  Copyright (C) 2015-2026 The Fluent Bit Authors
6
 *
7
 *  Licensed under the Apache License, Version 2.0 (the "License");
8
 *  you may not use this file except in compliance with the License.
9
 *  You may obtain a copy of the License at
10
 *
11
 *      http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 *  Unless required by applicable law or agreed to in writing, software
14
 *  distributed under the License is distributed on an "AS IS" BASIS,
15
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 *  See the License for the specific language governing permissions and
17
 *  limitations under the License.
18
 */
19
20
#include <fluent-bit/flb_output_plugin.h>
21
#include <fluent-bit/flb_fstore.h>
22
#include <fluent-bit/flb_time.h>
23
#include <cfl/cfl_atomic.h>
24
25
#include <inttypes.h>
26
27
#include "s3.h"
28
#include "s3_store.h"
29
30
static int counter_add(uint64_t *counter, uint64_t increment,
31
                       uint64_t *new_value)
32
0
{
33
0
    uint64_t current_value;
34
0
    uint64_t updated_value;
35
36
0
    while (FLB_TRUE) {
37
0
        current_value = cfl_atomic_load(counter);
38
0
        if (increment > UINT64_MAX - current_value) {
39
0
            return -1;
40
0
        }
41
42
0
        updated_value = current_value + increment;
43
0
        if (cfl_atomic_compare_exchange(counter, current_value, updated_value)) {
44
0
            if (new_value != NULL) {
45
0
                *new_value = updated_value;
46
0
            }
47
0
            return 0;
48
0
        }
49
0
    }
50
0
}
51
52
static int counter_subtract(uint64_t *counter, uint64_t decrement,
53
                            uint64_t *previous_value)
54
0
{
55
0
    int underflow;
56
0
    uint64_t current_value;
57
0
    uint64_t updated_value;
58
59
0
    while (FLB_TRUE) {
60
0
        current_value = cfl_atomic_load(counter);
61
0
        underflow = current_value < decrement;
62
0
        if (underflow == FLB_TRUE) {
63
0
            updated_value = 0;
64
0
        }
65
0
        else {
66
0
            updated_value = current_value - decrement;
67
0
        }
68
69
0
        if (cfl_atomic_compare_exchange(counter, current_value, updated_value)) {
70
0
            if (previous_value != NULL) {
71
0
                *previous_value = current_value;
72
0
            }
73
0
            return underflow == FLB_TRUE ? -1 : 0;
74
0
        }
75
0
    }
76
0
}
77
78
static int buffer_size_reserve(struct flb_s3 *ctx, size_t bytes,
79
                               uint64_t *current_value,
80
                               uint64_t *new_value)
81
0
{
82
0
    uint64_t buffer_size;
83
0
    uint64_t updated_size;
84
0
    uint64_t increment;
85
86
0
    increment = (uint64_t) bytes;
87
88
0
    while (FLB_TRUE) {
89
0
        buffer_size = cfl_atomic_load(&ctx->current_buffer_size);
90
0
        if (current_value != NULL) {
91
0
            *current_value = buffer_size;
92
0
        }
93
94
0
        if (increment > UINT64_MAX - buffer_size) {
95
0
            return -1;
96
0
        }
97
98
0
        updated_size = buffer_size + increment;
99
0
        if (ctx->store_dir_limit_size > 0 &&
100
0
            updated_size >= (uint64_t) ctx->store_dir_limit_size) {
101
0
            return -1;
102
0
        }
103
104
0
        if (cfl_atomic_compare_exchange(&ctx->current_buffer_size,
105
0
                                        buffer_size, updated_size)) {
106
0
            if (new_value != NULL) {
107
0
                *new_value = updated_size;
108
0
            }
109
0
            return 0;
110
0
        }
111
0
    }
112
0
}
113
114
static int quarantine_size_reserve(struct flb_s3 *ctx, uint64_t bytes)
115
0
{
116
0
    uint64_t buffer_size;
117
0
    uint64_t updated_size;
118
119
0
    while (FLB_TRUE) {
120
0
        buffer_size = cfl_atomic_load(&ctx->quarantine_buffer_size);
121
0
        if (bytes > UINT64_MAX - buffer_size) {
122
0
            return -1;
123
0
        }
124
125
0
        updated_size = buffer_size + bytes;
126
0
        if (ctx->quarantine_dir_limit_size > 0 &&
127
0
            updated_size > (uint64_t) ctx->quarantine_dir_limit_size) {
128
0
            return -1;
129
0
        }
130
131
0
        if (cfl_atomic_compare_exchange(&ctx->quarantine_buffer_size,
132
0
                                        buffer_size, updated_size)) {
133
0
            return 0;
134
0
        }
135
0
    }
136
0
}
137
138
static void buffer_size_release(struct flb_s3 *ctx, uint64_t bytes)
139
0
{
140
0
    int ret;
141
0
    uint64_t previous_value;
142
143
0
    ret = counter_subtract(&ctx->current_buffer_size, bytes, &previous_value);
144
0
    if (ret < 0) {
145
0
        flb_plg_warn(ctx->ins,
146
0
                     "buffer accounting mismatch: current_buffer_size=%" PRIu64
147
0
                     ", reclaim_size=%" PRIu64 "; clamping to zero",
148
0
                     previous_value, bytes);
149
0
    }
150
0
}
151
152
static int s3_store_under_travis_ci()
153
0
{
154
155
0
    if (getenv("CI") != NULL && getenv("TRAVIS") != NULL) {
156
0
        return FLB_TRUE;
157
0
    }
158
159
0
    return FLB_FALSE;
160
0
}
161
162
/*
163
 * Simple and fast hashing algorithm to create keys in the local buffer
164
 */
165
static flb_sds_t gen_store_filename(const char *tag)
166
0
{
167
0
    int c;
168
0
    unsigned long hash = 5381;
169
0
    unsigned long hash2 = 5381;
170
0
    flb_sds_t hash_str;
171
0
    flb_sds_t tmp;
172
0
    struct flb_time tm;
173
174
    /* get current time */
175
0
    flb_time_get(&tm);
176
177
    /* compose hash */
178
0
    while ((c = *tag++)) {
179
0
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
180
0
    }
181
0
    hash2 = (unsigned long) hash2 * tm.tm.tv_sec * tm.tm.tv_nsec;
182
183
    /* flb_sds_printf allocs if the incoming sds is not at least 64 bytes */
184
0
    hash_str = flb_sds_create_size(64);
185
0
    if (!hash_str) {
186
0
        flb_errno();
187
0
        return NULL;
188
0
    }
189
0
    tmp = flb_sds_printf(&hash_str, "%lu-%lu", hash, hash2);
190
0
    if (!tmp) {
191
0
        flb_errno();
192
0
        flb_sds_destroy(hash_str);
193
0
        return NULL;
194
0
    }
195
0
    hash_str = tmp;
196
197
0
    return hash_str;
198
0
}
199
200
/* Retrieve a candidate s3 local file using the tag */
201
struct s3_file *s3_store_file_get(struct flb_s3 *ctx, const char *tag,
202
                                  int tag_len)
203
0
{
204
0
    struct mk_list *head;
205
0
    struct mk_list *tmp;
206
0
    struct flb_fstore_file *fsf = NULL;
207
0
    struct s3_file *s3_file;
208
209
    /*
210
     * Based in the current ctx->stream_name, locate a candidate file to
211
     * store the incoming data using as a lookup pattern the content Tag.
212
     */
213
0
    mk_list_foreach_safe(head, tmp, &ctx->stream_active->files) {
214
0
        fsf = mk_list_entry(head, struct flb_fstore_file, _head);
215
216
        /* skip and warn on partially initialized chunks */
217
0
        if (fsf->data == NULL) {
218
0
            flb_plg_warn(ctx->ins, "BAD: found flb_fstore_file with NULL data reference, tag=%s, file=%s, will try to delete", tag, fsf->name);
219
0
            flb_fstore_file_delete(ctx->fs, fsf);
220
0
            fsf = NULL;
221
0
            continue;
222
0
        }
223
224
0
        if (fsf->meta_size != tag_len) {
225
0
            fsf = NULL;
226
0
            continue;
227
0
        }
228
229
        /* skip locked chunks */
230
0
        s3_file = fsf->data;
231
0
        if (s3_file->locked == FLB_TRUE) {
232
0
            fsf = NULL;
233
0
            continue;
234
0
        }
235
236
        /* compare meta and tag */
237
0
        if (strncmp((char *) fsf->meta_buf, tag, tag_len) == 0) {
238
0
            break;
239
0
        }
240
241
        /* not found, invalidate the reference */
242
0
        fsf = NULL;
243
0
    }
244
245
0
    if (!fsf) {
246
0
        return NULL;
247
0
    }
248
249
0
    s3_file = fsf->data;
250
251
0
    return s3_file;
252
0
}
253
254
/* Append data to a new or existing fstore file */
255
int s3_store_buffer_put(struct flb_s3 *ctx, struct s3_file *s3_file,
256
                        const char *tag, int tag_len,
257
                        char *data, size_t bytes,
258
                        time_t file_first_log_time)
259
0
{
260
0
    int ret;
261
0
    int result;
262
0
    flb_sds_t name;
263
0
    struct flb_fstore_file *fsf;
264
0
    uint64_t current_buffer_size;
265
0
    uint64_t new_buffer_size;
266
267
0
    result = -1;
268
0
    ret = buffer_size_reserve(ctx, bytes, &current_buffer_size,
269
0
                              &new_buffer_size);
270
0
    if (ret < 0) {
271
0
        flb_plg_error(ctx->ins,
272
0
                      "Buffer is full: current_buffer_size=%" PRIu64
273
0
                      ", new_data=%zu, store_dir_limit_size=%zu bytes",
274
0
                      current_buffer_size, bytes, ctx->store_dir_limit_size);
275
0
        goto done;
276
0
    }
277
278
    /* If no target file was found, create a new one */
279
0
    if (!s3_file) {
280
0
        name = gen_store_filename(tag);
281
0
        if (!name) {
282
0
            flb_plg_error(ctx->ins, "could not generate chunk file name");
283
0
            buffer_size_release(ctx, bytes);
284
0
            goto done;
285
0
        }
286
287
        /* Create the file */
288
0
        fsf = flb_fstore_file_create(ctx->fs, ctx->stream_active, name, bytes);
289
0
        if (!fsf) {
290
0
            flb_plg_error(ctx->ins, "could not create the file '%s' in the store",
291
0
                          name);
292
0
            flb_sds_destroy(name);
293
0
            buffer_size_release(ctx, bytes);
294
0
            goto done;
295
0
        }
296
0
        flb_sds_destroy(name);
297
298
        /* Write tag as metadata */
299
0
        ret = flb_fstore_file_meta_set(ctx->fs, fsf, (char *) tag, tag_len);
300
0
        if (ret == -1) {
301
0
            flb_plg_error(ctx->ins, "error writing tag metadata");
302
0
            flb_plg_warn(ctx->ins, "Deleting buffer file because metadata could not be written");
303
0
            flb_fstore_file_delete(ctx->fs, fsf);
304
0
            buffer_size_release(ctx, bytes);
305
0
            goto done;
306
0
        }
307
308
        /* Allocate local context */
309
0
        s3_file = flb_calloc(1, sizeof(struct s3_file));
310
0
        if (!s3_file) {
311
0
            flb_errno();
312
0
            flb_plg_error(ctx->ins, "cannot allocate s3 file context");
313
0
            flb_plg_warn(ctx->ins, "Deleting buffer file because S3 context creation failed");
314
0
            flb_fstore_file_delete(ctx->fs, fsf);
315
0
            buffer_size_release(ctx, bytes);
316
0
            goto done;
317
0
        }
318
0
        s3_file->fsf = fsf;
319
0
        s3_file->upload_scan_id = ctx->upload_scan_id;
320
0
        s3_file->first_log_time = file_first_log_time;
321
0
        s3_file->create_time = time(NULL);
322
323
        /* Use fstore opaque 'data' reference to keep our context */
324
0
        fsf->data = s3_file;
325
0
    }
326
0
    else {
327
0
        fsf = s3_file->fsf;
328
0
    }
329
330
    /* Append data to the target file */
331
0
    ret = flb_fstore_file_append(fsf, data, bytes);
332
0
    if (ret != 0) {
333
0
        flb_plg_error(ctx->ins, "error writing data to local s3 file");
334
0
        buffer_size_release(ctx, bytes);
335
0
        goto done;
336
0
    }
337
0
    ret = counter_add(&s3_file->size, (uint64_t) bytes, NULL);
338
0
    if (ret < 0) {
339
0
        flb_plg_error(ctx->ins, "local s3 file size accounting overflow");
340
0
    }
341
342
    /* if buffer is 95% full, warn user */
343
0
    if (ctx->store_dir_limit_size > 0 &&
344
0
        new_buffer_size > (uint64_t) ctx->store_dir_limit_size -
345
0
                          (ctx->store_dir_limit_size / 20)) {
346
0
        flb_plg_warn(ctx->ins,
347
0
                     "Buffer is almost full: current_buffer_size=%" PRIu64
348
0
                     ", store_dir_limit_size=%zu bytes",
349
0
                     new_buffer_size, ctx->store_dir_limit_size);
350
0
    }
351
352
0
    result = 0;
353
354
0
done:
355
0
    return result;
356
0
}
357
358
static ssize_t restored_file_size_get(struct flb_s3 *ctx,
359
                                      struct flb_fstore_file *fsf)
360
0
{
361
0
    int ret;
362
0
    int restore_down;
363
0
    ssize_t file_size;
364
365
0
    restore_down = FLB_FALSE;
366
367
0
    if (cio_chunk_is_up(fsf->chunk) == CIO_FALSE) {
368
0
        ret = cio_chunk_up_force(fsf->chunk);
369
0
        if (ret != CIO_OK) {
370
0
            flb_plg_error(ctx->ins,
371
0
                          "cannot load restored S3 chunk '%s' to determine its size",
372
0
                          fsf->name);
373
0
            return -1;
374
0
        }
375
0
        restore_down = FLB_TRUE;
376
0
    }
377
378
0
    file_size = cio_chunk_get_content_size(fsf->chunk);
379
380
0
    if (restore_down == FLB_TRUE) {
381
0
        ret = cio_chunk_down(fsf->chunk);
382
0
        if (ret != CIO_OK) {
383
0
            flb_plg_warn(ctx->ins,
384
0
                         "cannot return restored S3 chunk '%s' to the down state",
385
0
                         fsf->name);
386
0
        }
387
0
    }
388
389
0
    return file_size;
390
0
}
391
392
static int set_files_context(struct flb_s3 *ctx)
393
0
{
394
0
    struct mk_list *head;
395
0
    struct mk_list *f_head;
396
0
    struct flb_fstore_stream *fs_stream;
397
0
    struct flb_fstore_file *fsf;
398
0
    struct s3_file *s3_file;
399
0
    ssize_t file_size;
400
401
0
    mk_list_foreach(head, &ctx->fs->streams) {
402
0
        fs_stream = mk_list_entry(head, struct flb_fstore_stream, _head);
403
404
        /* skip multi-upload */
405
0
        if (fs_stream == ctx->stream_upload) {
406
0
            continue;
407
0
        }
408
409
0
        mk_list_foreach(f_head, &fs_stream->files) {
410
0
            fsf = mk_list_entry(f_head, struct flb_fstore_file, _head);
411
0
            if (fsf->data) {
412
0
                continue;
413
0
            }
414
415
            /* Allocate local context */
416
0
            s3_file = flb_calloc(1, sizeof(struct s3_file));
417
0
            if (!s3_file) {
418
0
                flb_errno();
419
0
                flb_plg_error(ctx->ins, "cannot allocate s3 file context");
420
0
                continue;
421
0
            }
422
0
            s3_file->fsf = fsf;
423
0
            s3_file->first_log_time = time(NULL);
424
0
            s3_file->create_time = time(NULL);
425
426
0
            file_size = restored_file_size_get(ctx, fsf);
427
0
            if (file_size > 0) {
428
0
                cfl_atomic_store(&s3_file->size, (uint64_t) file_size);
429
430
0
                if (fs_stream == ctx->stream_quarantine) {
431
0
                    if (counter_add(&ctx->quarantine_buffer_size,
432
0
                                    (uint64_t) file_size, NULL) < 0) {
433
0
                        cfl_atomic_store(&ctx->quarantine_buffer_size,
434
0
                                         UINT64_MAX);
435
0
                    }
436
0
                }
437
0
                else if (counter_add(&ctx->current_buffer_size,
438
0
                                     (uint64_t) file_size, NULL) < 0) {
439
0
                    cfl_atomic_store(&ctx->current_buffer_size, UINT64_MAX);
440
0
                }
441
0
            }
442
443
            /* Use fstore opaque 'data' reference to keep our context */
444
0
            fsf->data = s3_file;
445
0
        }
446
0
    }
447
448
0
    return 0;
449
0
}
450
451
/* Initialize filesystem storage for S3 plugin */
452
int s3_store_init(struct flb_s3 *ctx)
453
0
{
454
0
    int type;
455
0
    time_t now;
456
0
    char tmp[64];
457
0
    struct tm *tm;
458
0
    struct flb_fstore *fs;
459
0
    struct flb_fstore_stream *fs_stream;
460
461
0
    if (s3_store_under_travis_ci() == FLB_TRUE) {
462
0
        type = FLB_FSTORE_MEM;
463
0
        flb_plg_warn(ctx->ins, "Travis CI test, using s3 store memory backend");
464
0
    }
465
0
    else {
466
0
        type = FLB_FSTORE_FS;
467
0
    }
468
469
    /* Initialize the storage context */
470
0
    fs = flb_fstore_create(ctx->buffer_dir, type);
471
0
    if (!fs) {
472
0
        return -1;
473
0
    }
474
0
    ctx->fs = fs;
475
476
    /*
477
     * On every start we create a new stream, this stream in the file system
478
     * is directory with the name using the date like '2020-10-03T13:00:02'. So
479
     * all the 'new' data that is generated on this process is stored there.
480
     *
481
     * Note that previous data in similar directories from previous runs is
482
     * considered backlog data, in the S3 plugin we need to differenciate the
483
     * new v/s the older buffered data.
484
     *
485
     * Compose a stream name...
486
     */
487
0
    now = time(NULL);
488
0
    tm = localtime(&now);
489
490
#ifdef FLB_SYSTEM_WINDOWS
491
    /* Windows does not allow ':' in directory names */
492
    strftime(tmp, sizeof(tmp) - 1, "%Y-%m-%dT%H-%M-%S", tm);
493
#else
494
0
    strftime(tmp, sizeof(tmp) - 1, "%Y-%m-%dT%H:%M:%S", tm);
495
0
#endif
496
497
    /* Create the stream */
498
0
    fs_stream = flb_fstore_stream_create(ctx->fs, tmp);
499
0
    if (!fs_stream) {
500
        /* Upon exception abort */
501
0
        flb_plg_error(ctx->ins, "could not initialize active stream: %s", tmp);
502
0
        flb_fstore_destroy(fs);
503
0
        ctx->fs = NULL;
504
0
        return -1;
505
0
    }
506
0
    ctx->stream_active = fs_stream;
507
508
    /* Multipart upload stream */
509
0
    fs_stream = flb_fstore_stream_create(ctx->fs, "multipart_upload_metadata");
510
0
    if (!fs_stream) {
511
0
        flb_plg_error(ctx->ins, "could not initialize multipart_upload stream");
512
0
        flb_fstore_destroy(fs);
513
0
        ctx->fs = NULL;
514
0
        return -1;
515
0
    }
516
0
    ctx->stream_upload = fs_stream;
517
518
    /* Terminal quarantine stream */
519
0
    fs_stream = flb_fstore_stream_create(ctx->fs, "quarantine");
520
0
    if (!fs_stream) {
521
0
        flb_plg_error(ctx->ins, "could not initialize quarantine stream");
522
0
        flb_fstore_destroy(fs);
523
0
        ctx->fs = NULL;
524
0
        return -1;
525
0
    }
526
0
    ctx->stream_quarantine = fs_stream;
527
528
0
    set_files_context(ctx);
529
0
    return 0;
530
0
}
531
532
int s3_store_exit(struct flb_s3 *ctx)
533
0
{
534
0
    struct mk_list *head;
535
0
    struct mk_list *f_head;
536
0
    struct flb_fstore_stream *fs_stream;
537
0
    struct flb_fstore_file *fsf;
538
0
    struct s3_file *s3_file;
539
540
0
    if (!ctx->fs) {
541
0
        return 0;
542
0
    }
543
544
    /* release local context on non-multi upload files */
545
0
    mk_list_foreach(head, &ctx->fs->streams) {
546
0
        fs_stream = mk_list_entry(head, struct flb_fstore_stream, _head);
547
0
        if (fs_stream == ctx->stream_upload) {
548
0
            continue;
549
0
        }
550
551
0
        mk_list_foreach(f_head, &fs_stream->files) {
552
0
            fsf = mk_list_entry(f_head, struct flb_fstore_file, _head);
553
0
            if (fsf->data != NULL) {
554
0
                s3_file = fsf->data;
555
0
                flb_sds_destroy(s3_file->file_path);
556
0
                flb_free(s3_file);
557
0
            }
558
0
        }
559
0
    }
560
561
0
    if (ctx->fs) {
562
0
        flb_fstore_destroy(ctx->fs);
563
0
    }
564
0
    return 0;
565
0
}
566
567
/*
568
 * Check if the store has data. This function is only used on plugin
569
 * initialization
570
 */
571
int s3_store_has_data(struct flb_s3 *ctx)
572
0
{
573
0
    struct mk_list *head;
574
0
    struct flb_fstore_stream *fs_stream;
575
576
0
    if (!ctx->fs) {
577
0
        return FLB_FALSE;
578
0
    }
579
580
0
    mk_list_foreach(head, &ctx->fs->streams) {
581
        /* skip multi upload stream */
582
0
        fs_stream = mk_list_entry(head, struct flb_fstore_stream, _head);
583
0
        if (fs_stream == ctx->stream_upload ||
584
0
            fs_stream == ctx->stream_quarantine) {
585
0
            continue;
586
0
        }
587
588
0
        if (mk_list_size(&fs_stream->files) > 0) {
589
0
            return FLB_TRUE;
590
0
        }
591
0
    }
592
593
0
    return FLB_FALSE;
594
0
}
595
596
int s3_store_has_uploads(struct flb_s3 *ctx)
597
0
{
598
0
    if (!ctx || !ctx->stream_upload) {
599
0
        return FLB_FALSE;
600
0
    }
601
602
0
    if (mk_list_size(&ctx->stream_upload->files) > 0) {
603
0
        return FLB_TRUE;
604
0
    }
605
606
0
    return FLB_FALSE;
607
0
}
608
609
int s3_store_file_inactive(struct flb_s3 *ctx, struct s3_file *s3_file)
610
0
{
611
0
    int ret;
612
0
    struct flb_fstore_file *fsf;
613
614
0
    fsf = s3_file->fsf;
615
0
    flb_free(s3_file);
616
0
    ret = flb_fstore_file_inactive(ctx->fs, fsf);
617
618
0
    return ret;
619
0
}
620
621
int s3_store_file_quarantine(struct flb_s3 *ctx, struct s3_file *s3_file)
622
0
{
623
0
    struct flb_fstore_file *fsf;
624
0
    struct flb_fstore_file *qfsf;
625
0
    void *buf;
626
0
    size_t size;
627
0
    int ret;
628
629
0
    fsf = s3_file->fsf;
630
0
    ret = flb_fstore_file_content_copy(ctx->fs, fsf, &buf, &size);
631
0
    if (ret < 0) {
632
0
        return -1;
633
0
    }
634
635
0
    ret = quarantine_size_reserve(ctx, (uint64_t) size);
636
0
    if (ret < 0) {
637
0
        flb_free(buf);
638
0
        return S3_STORE_QUARANTINE_FULL;
639
0
    }
640
641
0
    qfsf = flb_fstore_file_create(ctx->fs, ctx->stream_quarantine, fsf->name, size);
642
0
    if (qfsf == NULL) {
643
0
        flb_free(buf);
644
0
        counter_subtract(&ctx->quarantine_buffer_size, (uint64_t) size, NULL);
645
0
        return -1;
646
0
    }
647
648
0
    if (fsf->meta_buf != NULL && fsf->meta_size > 0) {
649
0
        ret = flb_fstore_file_meta_set(ctx->fs, qfsf, fsf->meta_buf, fsf->meta_size);
650
0
        if (ret < 0) {
651
0
            flb_free(buf);
652
0
            flb_fstore_file_delete(ctx->fs, qfsf);
653
0
            counter_subtract(&ctx->quarantine_buffer_size, (uint64_t) size, NULL);
654
0
            return -1;
655
0
        }
656
0
    }
657
658
0
    ret = flb_fstore_file_append(qfsf, buf, size);
659
0
    flb_free(buf);
660
0
    if (ret < 0) {
661
0
        flb_fstore_file_delete(ctx->fs, qfsf);
662
0
        counter_subtract(&ctx->quarantine_buffer_size, (uint64_t) size, NULL);
663
0
        return -1;
664
0
    }
665
666
0
    flb_fstore_file_delete(ctx->fs, fsf);
667
0
    buffer_size_release(ctx, s3_store_file_size_get(s3_file));
668
0
    flb_free(s3_file);
669
0
    return 0;
670
0
}
671
672
uint64_t s3_store_file_size_get(struct s3_file *s3_file)
673
0
{
674
0
    return cfl_atomic_load(&s3_file->size);
675
0
}
676
677
int s3_store_file_delete(struct flb_s3 *ctx, struct s3_file *s3_file)
678
0
{
679
0
    struct flb_fstore_file *fsf;
680
0
    uint64_t file_size;
681
682
0
    fsf = s3_file->fsf;
683
0
    file_size = s3_store_file_size_get(s3_file);
684
0
    buffer_size_release(ctx, file_size);
685
686
    /* permanent deletion */
687
0
    flb_fstore_file_delete(ctx->fs, fsf);
688
0
    flb_free(s3_file);
689
690
0
    return 0;
691
0
}
692
693
int s3_store_file_read(struct flb_s3 *ctx, struct s3_file *s3_file,
694
                               char **out_buf, size_t *out_size)
695
0
{
696
0
    int ret;
697
698
0
    ret = flb_fstore_file_content_copy(ctx->fs, s3_file->fsf,
699
0
                                       (void **) out_buf, out_size);
700
0
    return ret;
701
0
}
702
703
int s3_store_file_upload_read(struct flb_s3 *ctx, struct flb_fstore_file *fsf,
704
                              char **out_buf, size_t *out_size)
705
0
{
706
0
    int ret;
707
708
0
    ret = flb_fstore_file_content_copy(ctx->fs, fsf,
709
0
                                       (void **) out_buf, out_size);
710
0
    return ret;
711
0
}
712
713
struct flb_fstore_file *s3_store_file_upload_get(struct flb_s3 *ctx,
714
                                                 char *key, int key_len)
715
0
{
716
0
    struct mk_list *head;
717
0
    struct flb_fstore_file *fsf = NULL;
718
719
0
    mk_list_foreach(head, &ctx->stream_upload->files) {
720
0
        fsf = mk_list_entry(head, struct flb_fstore_file, _head);
721
0
        if (fsf->meta_buf == NULL) {
722
0
            continue;
723
0
        }
724
725
0
        if (fsf->meta_size != key_len ){
726
0
            continue;
727
0
        }
728
729
0
        if (strncmp(fsf->meta_buf, key, key_len) == 0) {
730
0
            break;
731
0
        }
732
0
        fsf = NULL;
733
0
    }
734
735
0
    return fsf;
736
0
}
737
738
/* param fsf can NULL if the file has not yet been created */
739
int s3_store_file_upload_put(struct flb_s3 *ctx,
740
                             struct flb_fstore_file *fsf, flb_sds_t key,
741
                             flb_sds_t data)
742
0
{
743
0
    int ret;
744
0
    flb_sds_t name;
745
746
    /* If no target file was found, create a new one */
747
0
    if (!fsf) {
748
0
        name = gen_store_filename(key);
749
0
        if (!name) {
750
0
            flb_plg_error(ctx->ins, "could not generate chunk file name");
751
0
            return -1;
752
0
        }
753
754
        /* Create the file */
755
0
        fsf = flb_fstore_file_create(ctx->fs, ctx->stream_upload, name, flb_sds_len(data));
756
0
        if (!fsf) {
757
0
            flb_plg_error(ctx->ins, "could not create the file '%s' in the upload store",
758
0
                          name);
759
0
            flb_sds_destroy(name);
760
0
            return -1;
761
0
        }
762
0
        flb_sds_destroy(name);
763
764
        /* Write key as metadata */
765
0
        ret = flb_fstore_file_meta_set(ctx->fs, fsf,
766
0
                                    key, flb_sds_len(key));
767
0
        if (ret == -1) {
768
0
            flb_plg_error(ctx->ins, "error writing upload metadata");
769
0
            flb_plg_warn(ctx->ins, "Deleting s3 upload cache file because metadata could not be written");
770
0
            flb_fstore_file_delete(ctx->fs, fsf);
771
0
            return -1;
772
0
        }
773
0
    }
774
775
    /* Append data to the target file */
776
0
    ret = flb_fstore_file_append(fsf, data, flb_sds_len(data));
777
0
    if (ret != 0) {
778
0
        flb_plg_error(ctx->ins, "error writing data to local s3 file");
779
0
        return -1;
780
0
    }
781
782
0
    return 0;
783
0
}
784
785
int s3_store_file_upload_delete(struct flb_s3 *ctx, struct flb_fstore_file *fsf)
786
0
{
787
    /* permanent deletion */
788
0
    flb_fstore_file_delete(ctx->fs, fsf);
789
0
    return 0;
790
0
}
791
792
/* Always set an updated copy of metadata into the fs_store_file entry */
793
int s3_store_file_meta_get(struct flb_s3 *ctx, struct flb_fstore_file *fsf)
794
0
{
795
0
    return flb_fstore_file_meta_get(ctx->fs, fsf);
796
0
}
797
798
void s3_store_file_lock(struct s3_file *s3_file)
799
0
{
800
0
    s3_file->locked = FLB_TRUE;
801
0
}
802
803
void s3_store_file_unlock(struct s3_file *s3_file)
804
0
{
805
    s3_file->locked = FLB_FALSE;
806
0
}