/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, ¤t_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 | } |