diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 12:08:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 12:08:18 +0000 |
commit | 5da14042f70711ea5cf66e034699730335462f66 (patch) | |
tree | 0f6354ccac934ed87a2d555f45be4c831cf92f4a /src/fluent-bit/lib/chunkio/tests/fs.c | |
parent | Releasing debian version 1.44.3-2. (diff) | |
download | netdata-5da14042f70711ea5cf66e034699730335462f66.tar.xz netdata-5da14042f70711ea5cf66e034699730335462f66.zip |
Merging upstream version 1.45.3+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/fluent-bit/lib/chunkio/tests/fs.c')
-rw-r--r-- | src/fluent-bit/lib/chunkio/tests/fs.c | 980 |
1 files changed, 980 insertions, 0 deletions
diff --git a/src/fluent-bit/lib/chunkio/tests/fs.c b/src/fluent-bit/lib/chunkio/tests/fs.c new file mode 100644 index 000000000..a976f46d1 --- /dev/null +++ b/src/fluent-bit/lib/chunkio/tests/fs.c @@ -0,0 +1,980 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Chunk I/O + * ========= + * Copyright 2018 Eduardo Silva <eduardo@monkey.io> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _WIN32 +#include <sys/mman.h> +#include <arpa/inet.h> +#endif +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <chunkio/chunkio.h> +#include <chunkio/cio_log.h> +#include <chunkio/cio_scan.h> +#include <chunkio/cio_file.h> +#include <chunkio/cio_meta.h> +#include <chunkio/cio_stream.h> +#include <chunkio/cio_utils.h> +#include <chunkio/cio_error.h> +#include <chunkio/cio_file_native.h> + +#include "cio_tests_internal.h" + +#define CIO_ENV "/tmp/cio-fs-test/" +#define CIO_FILE_400KB CIO_TESTS_DATA_PATH "/data/400kb.txt" + + +/* Logging callback, once called it just turn on the log_check flag */ +static int log_cb(struct cio_ctx *ctx, int level, const char *file, int line, + char *str) +{ + (void) ctx; + + printf("[cio-test-fs] %-60s => %s:%i\n", str, file, line); + return 0; +} + +/* Test API generating files to the file system and then scanning them back */ +static void test_fs_write() +{ + int i; + int ret; + int len; + int err; + int n_files = 100; + int flags; + char *in_data; + size_t in_size; + char tmp[255]; + struct cio_ctx *ctx; + struct cio_stream *stream; + struct cio_chunk *chunk; + struct cio_chunk **carr; + struct cio_options cio_opts; + + /* Dummy break line for clarity on acutest output */ + printf("\n"); + + flags = CIO_CHECKSUM; + + cio_options_init(&cio_opts); + + cio_opts.root_path = CIO_ENV; + cio_opts.log_cb = log_cb; + cio_opts.flags = flags; + + /* cleanup environment */ + cio_utils_recursive_delete(CIO_ENV); + + /* Create main context */ + ctx = cio_create(&cio_opts); + TEST_CHECK(ctx != NULL); + + /* Try to create a file with an invalid stream */ + chunk = cio_chunk_open(ctx, NULL, "invalid", 0, 0, &err); + TEST_CHECK(chunk == NULL); + + /* Check invalid stream */ + stream = cio_stream_create(ctx, "", CIO_STORE_FS); + TEST_CHECK(stream == NULL); + + /* Another invalid name */ + stream = cio_stream_create(ctx, "/", CIO_STORE_FS); + TEST_CHECK(stream == NULL); + + /* Create valid stream */ + stream = cio_stream_create(ctx, "test-write", CIO_STORE_FS); + TEST_CHECK(stream != NULL); + + /* + * Load sample data file and with the same content through multiple write + * operations generating other files. + */ + ret = cio_utils_read_file(CIO_FILE_400KB, &in_data, &in_size); + TEST_CHECK(ret == 0); + if (ret == -1) { + cio_destroy(ctx); + exit(EXIT_FAILURE); + } + + /* Number of test files to create */ + n_files = 100; + + /* Allocate files array */ + carr = calloc(1, sizeof(struct cio_file) * n_files); + if (!carr) { + perror("calloc"); + exit(EXIT_FAILURE); + } + + + for (i = 0; i < n_files; i++) { + len = snprintf(tmp, sizeof(tmp), "api-test-%04i.txt", i); + carr[i] = cio_chunk_open(ctx, stream, tmp, CIO_OPEN, 1000000, &err); + + if (carr[i] == NULL) { + continue; + } + + /* Check that next buffers are 'down' */ + if (i >= CIO_MAX_CHUNKS_UP) { + ret = cio_chunk_is_up(carr[i]); + TEST_CHECK(ret == CIO_FALSE); + cio_chunk_up_force(carr[i]); + } + + cio_chunk_write(carr[i], in_data, in_size); + cio_chunk_write(carr[i], in_data, in_size); + + /* update metadata */ + cio_meta_write(carr[i], tmp, len); + + /* continue appending data to content area */ + cio_chunk_write(carr[i], in_data, in_size); + cio_chunk_write(carr[i], in_data, in_size); + cio_chunk_write(carr[i], in_data, in_size); + + /* sync to disk */ + cio_chunk_sync(carr[i]); + } + + /* Release file data and destroy context */ + free(carr); + free(in_data); + cio_destroy(ctx); + + cio_options_init(&cio_opts); + + cio_opts.root_path = CIO_ENV; + cio_opts.log_cb = log_cb; + cio_opts.log_level = CIO_LOG_INFO; + cio_opts.flags = flags; + + /* Create new context using the data generated above */ + ctx = cio_create(&cio_opts); + TEST_CHECK(ctx != NULL); + cio_scan_dump(ctx); + cio_destroy(ctx); +} + +/* + * Create one file chunk and check it updated sha1 after a couple of writes + * and sync. + */ +static void test_fs_checksum() +{ + int ret; + int err; + int flags; + char *in_data; + char *f_hash; + size_t in_size; + uint32_t val; + struct cio_ctx *ctx; + struct cio_stream *stream; + struct cio_chunk *chunk; + struct cio_options cio_opts; + + /* + * crc32 checksums + * =============== + */ + + /* Empty file */ + char crc32_test1[] = { + 0xff, 0x12, 0xd9, 0x41, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + /* CRC32 of 2 zero bytes + content of data/400kb.txt file */ + char crc32_test2[] = { + 0x67, 0xfa, 0x3c, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + flags = CIO_CHECKSUM; + + /* Dummy break line for clarity on acutest output */ + printf("\n"); + + /* cleanup environment */ + cio_utils_recursive_delete(CIO_ENV); + + cio_options_init(&cio_opts); + + cio_opts.root_path = CIO_ENV; + cio_opts.log_cb = log_cb; + cio_opts.flags = flags; + + ctx = cio_create(&cio_opts); + TEST_CHECK(ctx != NULL); + + stream = cio_stream_create(ctx, "test-crc32", CIO_STORE_FS); + TEST_CHECK(stream != NULL); + + /* Load sample data file in memory */ + ret = cio_utils_read_file(CIO_FILE_400KB, &in_data, &in_size); + TEST_CHECK(ret == 0); + if (ret == -1) { + cio_destroy(ctx); + exit(EXIT_FAILURE); + } + + /* + * Test 1: + * - create one empty file + * - sync + * - validate crc32_test1 + */ + chunk = cio_chunk_open(ctx, stream, "test1.out", CIO_OPEN, 10, &err); + TEST_CHECK(chunk != NULL); + + /* Check default crc32() for an empty file after sync */ + f_hash = cio_chunk_hash(chunk); + TEST_CHECK(f_hash != NULL); + cio_chunk_sync(chunk); + + memcpy(&val, f_hash, sizeof(val)); + val = ntohl(val); + + ret = memcmp(&val, crc32_test1, 4); + TEST_CHECK(ret == 0); + + /* + * Test 2: + * - append content of 400kb.txt file to file context + * - validate file crc32 in mem is the same as crc_test1 + * - sync + * - validate file crc32 in mem is equal to sha_test2 + * + * note that the second sha1 calculation is done using the initial + * sha1 context so it skip old data to perform the verification. + */ + cio_chunk_write(chunk, in_data, in_size); + cio_chunk_sync(chunk); + + f_hash = cio_chunk_hash(chunk); + memcpy(&val, f_hash, sizeof(val)); + val = ntohl(val); + + ret = memcmp(&val, crc32_test2, 4); + TEST_CHECK(ret == 0); + + /* Release */ + cio_destroy(ctx); + free(in_data); +} + +/* + * Create one file chunk, do writes and invoke up()/down() calls, then validate + * it checksum. + */ +static void test_fs_up_down() +{ + int ret; + int err; + int flags; + char *in_data; + char *f_hash; + size_t in_size; + uint32_t val; + char path[1024]; + struct stat st; + struct cio_ctx *ctx; + struct cio_stream *stream; + struct cio_chunk *chunk; + struct cio_options cio_opts; + + /* + * crc32 checksums + * =============== + */ + + /* Empty file */ + char crc32_test1[] = { + 0xff, 0x12, 0xd9, 0x41, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + /* CRC32 of 2 zero bytes + content of data/400kb.txt file */ + char crc32_test2[] = { + 0x67, 0xfa, 0x3c, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + flags = CIO_CHECKSUM; + + /* Dummy break line for clarity on acutest output */ + printf("\n"); + + /* cleanup environment */ + cio_utils_recursive_delete(CIO_ENV); + + cio_options_init(&cio_opts); + + cio_opts.root_path = CIO_ENV; + cio_opts.log_cb = log_cb; + cio_opts.log_level = CIO_LOG_INFO; + cio_opts.flags = flags; + + ctx = cio_create(&cio_opts); + TEST_CHECK(ctx != NULL); + + stream = cio_stream_create(ctx, "test-crc32", CIO_STORE_FS); + TEST_CHECK(stream != NULL); + + /* Load sample data file in memory */ + ret = cio_utils_read_file(CIO_FILE_400KB, &in_data, &in_size); + TEST_CHECK(ret == 0); + if (ret == -1) { + cio_destroy(ctx); + exit(EXIT_FAILURE); + } + + /* + * Test 1: + * - create one empty file + * - sync + * - validate crc32_test1 + */ + chunk = cio_chunk_open(ctx, stream, "test1.out", CIO_OPEN, 10, &err); + TEST_CHECK(chunk != NULL); + + /* file down/up */ + TEST_CHECK(cio_chunk_is_up(chunk) == CIO_TRUE); + ret = cio_chunk_down(chunk); + + TEST_CHECK(ret == 0); + TEST_CHECK(cio_chunk_is_up(chunk) == CIO_FALSE); + ret = cio_chunk_up(chunk); + TEST_CHECK(ret == 0); + TEST_CHECK(cio_chunk_is_up(chunk) == CIO_TRUE); + + /* Check default crc32() for an empty file after sync */ + f_hash = cio_chunk_hash(chunk); + TEST_CHECK(f_hash != NULL); + cio_chunk_sync(chunk); + + memcpy(&val, f_hash, sizeof(val)); + val = ntohl(val); + + ret = memcmp(&val, crc32_test1, 4); + TEST_CHECK(ret == 0); + + /* + * Test 2: + * - append content of 400kb.txt file to file context + * - validate file crc32 in mem is the same as crc_test1 + * - sync + * - validate file crc32 in mem is equal to sha_test2 + * + * note that the second sha1 calculation is done using the initial + * sha1 context so it skip old data to perform the verification. + */ + cio_chunk_write(chunk, in_data, in_size); + + cio_chunk_sync(chunk); + + /* + * Bug https://github.com/fluent/fluent-bit/pull/3054#issuecomment-778831815 + * + * the fs_size cache value is not being updated after a sync, let's validate. + */ + snprintf(path, sizeof(path) - 1, "%s%s", CIO_ENV, "test-crc32/test1.out"); + ret = stat(path, &st); + TEST_CHECK(ret == 0); + TEST_CHECK(st.st_size == cio_chunk_get_real_size(chunk)); + + /* file down/up */ + TEST_CHECK(cio_chunk_is_up(chunk) == CIO_TRUE); + ret = cio_chunk_down(chunk); + TEST_CHECK(ret == 0); + TEST_CHECK(cio_chunk_is_up(chunk) == CIO_FALSE); + ret = cio_chunk_up(chunk); + TEST_CHECK(ret == 0); + TEST_CHECK(cio_chunk_is_up(chunk) == CIO_TRUE); + + f_hash = cio_chunk_hash(chunk); + memcpy(&val, f_hash, sizeof(val)); + val = ntohl(val); + + ret = memcmp(&val, crc32_test2, 4); + TEST_CHECK(ret == 0); + + /* Release */ + cio_destroy(ctx); + free(in_data); +} + +/* ref: https://github.com/edsiper/chunkio/pull/51 */ +static void test_issue_51() +{ + int fd; + int err; + struct cio_ctx *ctx; + struct cio_stream *stream; + struct cio_options cio_opts; + + /* Create a temporal storage */ + cio_options_init(&cio_opts); + + cio_opts.root_path = "tmp"; + cio_opts.log_cb = log_cb; + cio_opts.log_level = CIO_LOG_DEBUG; + cio_opts.flags = 0; + + ctx = cio_create(&cio_opts); + stream = cio_stream_create(ctx, "test", CIO_STORE_FS); + cio_chunk_open(ctx, stream, "c", CIO_OPEN, 1000, &err); + cio_destroy(ctx); + + /* Corrupt the file */ + fd = open("tmp/test/c", O_WRONLY); + TEST_CHECK(fd != -1); + if (fd == -1) { + perror("open"); + exit(1); + } + +#ifdef _WIN32 + _chsize(fd, 1); +#else + ftruncate(fd, 1); +#endif + + close(fd); + + /* Re-read the content */ + ctx = cio_create(&cio_opts); + + /* Upon scanning an existing stream, if not fixed, the program crashes */ + stream = cio_stream_create(ctx, "test", CIO_STORE_FS); + cio_chunk_open(ctx, stream, "c", CIO_OPEN, 1000, &err); + cio_destroy(ctx); +} + +/* ref: https://github.com/fluent/fluent-bit/2025 */ +static void test_issue_flb_2025() +{ + int i; + int ret; + int err; + int len; + char line[] = "this is a test line\n"; + struct cio_ctx *ctx; + struct cio_chunk *chunk; + struct cio_stream *stream; + struct cio_options cio_opts; + + cio_utils_recursive_delete("tmp"); + + /* Create a temporal storage */ + cio_options_init(&cio_opts); + + cio_opts.root_path = "tmp"; + cio_opts.log_cb = log_cb; + cio_opts.log_level = CIO_LOG_DEBUG; + cio_opts.flags = CIO_CHECKSUM; + + ctx = cio_create(&cio_opts); + stream = cio_stream_create(ctx, "test", CIO_STORE_FS); + chunk = cio_chunk_open(ctx, stream, "c", CIO_OPEN, 1000, &err); + TEST_CHECK(chunk != NULL); + if (!chunk) { + printf("cannot open chunk\n"); + exit(1); + } + + len = strlen(line); + for (i = 0; i < 1000; i++) { + ret = cio_chunk_write(chunk, line, len); + TEST_CHECK(ret == CIO_OK); + + ret = cio_chunk_down(chunk); + TEST_CHECK(ret == CIO_OK); + + ret = cio_chunk_up(chunk); + TEST_CHECK(ret == CIO_OK); + } + + cio_destroy(ctx); +} + +void test_fs_size_chunks_up() +{ + int i; + int ret; + int len; + int err; + int flags; + char line[] = "this is a test line\n"; + char name[32]; + size_t expected; + struct cio_ctx *ctx; + struct cio_chunk *chunk; + struct cio_chunk *chunk_tmp; + struct cio_stream *stream; + struct cio_options cio_opts; + + /* cleanup environment */ + cio_utils_recursive_delete(CIO_ENV); + + flags = CIO_CHECKSUM; + + cio_options_init(&cio_opts); + + cio_opts.root_path = CIO_ENV; + cio_opts.log_cb = log_cb; + cio_opts.log_level = CIO_LOG_INFO; + cio_opts.flags = flags; + + ctx = cio_create(&cio_opts); + TEST_CHECK(ctx != NULL); + + /* Set default number of chunks up */ + cio_set_max_chunks_up(ctx, 50); + + stream = cio_stream_create(ctx, "test_size_chunks_up", CIO_STORE_FS); + TEST_CHECK(stream != NULL); + + len = strlen(line); + for (i = 0; i < 100; i++) { + /* Create the chunk */ + snprintf(name, sizeof(name) - 1, "test-%i", i); + + chunk = cio_chunk_open(ctx, stream, name, CIO_OPEN, 1000, &err); + TEST_CHECK(chunk != NULL); + if (!chunk) { + exit(1); + } + + if (i < 50) { + /* First 50 chunks (0-49) will be in an 'up' state */ + ret = cio_chunk_is_up(chunk); + TEST_CHECK(ret == CIO_TRUE); + if (ret == CIO_FALSE) { + exit(1); + } + ret = cio_chunk_write(chunk, line, len); + TEST_CHECK(ret == CIO_OK); + + /* Check this chunk is in the 'chunks_up' list */ + chunk_tmp = mk_list_entry_last(&stream->chunks_up, + struct cio_chunk, + _state_head); + TEST_CHECK(chunk_tmp == chunk); + + /* Put the chunk down and now recheck 'chunks_down' list */ + ret = cio_chunk_down(chunk); + TEST_CHECK(ret == CIO_OK); + + /* Down list */ + chunk_tmp = mk_list_entry_last(&stream->chunks_down, + struct cio_chunk, + _state_head); + TEST_CHECK(chunk_tmp == chunk); + + /* Put the chunk UP again */ + ret = cio_chunk_up(chunk); + TEST_CHECK(ret == CIO_OK); + + /* Check this chunk is in the 'chunks_up' list */ + chunk_tmp = mk_list_entry_last(&stream->chunks_up, + struct cio_chunk, + _state_head); + TEST_CHECK(chunk_tmp == chunk); + } + else { + /* + * Remaining created chunks are in a down state, after creation + * this chunks must be linked in the struct cio_stream->chunks_down + * list. + */ + chunk_tmp = mk_list_entry_last(&stream->chunks_down, + struct cio_chunk, + _state_head); + TEST_CHECK(chunk_tmp == chunk); + } + } + + /* 50 chunks are up, each chunk contains 'len' bytes */ + expected = 50 * len; + TEST_CHECK(cio_stream_size_chunks_up(stream) == expected); + + /* Cleanup */ + cio_destroy(ctx); +} + +void test_issue_write_at() +{ + int ret; + int len; + int err; + char line[] = "this is a test line\n"; + struct cio_ctx *ctx; + struct cio_chunk *chunk; + struct cio_stream *stream; + struct cio_options cio_opts; + + /* cleanup environment */ + cio_utils_recursive_delete(CIO_ENV); + + /* create Chunk I/O context */ + cio_options_init(&cio_opts); + + cio_opts.root_path = CIO_ENV; + cio_opts.log_cb = log_cb; + cio_opts.log_level = CIO_LOG_INFO; + cio_opts.flags = CIO_CHECKSUM; + + ctx = cio_create(&cio_opts); + TEST_CHECK(ctx != NULL); + + /* Set default number of chunks up */ + cio_set_max_chunks_up(ctx, 50); + + /* create stream */ + stream = cio_stream_create(ctx, "test_write_at", CIO_STORE_FS); + TEST_CHECK(stream != NULL); + + /* create chunk */ + chunk = cio_chunk_open(ctx, stream, "test", CIO_OPEN, 1000, &err); + TEST_CHECK(chunk != NULL); + if (!chunk) { + exit(1); + } + + len = strlen(line); + + /* Write 3 lines */ + ret = cio_chunk_write(chunk, line, len); + TEST_CHECK(ret == CIO_OK); + + ret = cio_chunk_write(chunk, line, len); + TEST_CHECK(ret == CIO_OK); + + ret = cio_chunk_write(chunk, line, len); + TEST_CHECK(ret == CIO_OK); + + /* + * Write some content after the second line: this is the issue, when writing + * to a position lowest than the last offset the checksum is not updated, for + * hence after putting it down and up again, the checksym validation fails + * and we get in the wrong state. + */ + ret = cio_chunk_write_at(chunk, len * 2, "test\n", 5); + TEST_CHECK(ret == CIO_OK); + + /* Put the chunk down and up */ + ret = cio_chunk_down(chunk); + TEST_CHECK(ret == CIO_OK); + + /* Trigger the 'format check failed' error */ + ret = cio_chunk_up(chunk); + TEST_CHECK(ret == CIO_OK); + + /* + * Corrupt the CRC manually, alter the current CRC and write a byte + * to the chunk to get the checksum corruption. Here we expect two + * things: + * + * - when trying to put the chunk get CIO_CORRUPTED + * - check the error number, it must be CIO_ERR_BAD_CHECKSUM + * - memory map must be null and file descriptor must be in a closed state + */ + struct cio_file *cf = (struct cio_file *) chunk->backend; + cf->crc_cur = 10; + cio_chunk_write(chunk, "\0", 1); + + ret = cio_chunk_down(chunk); + TEST_CHECK(ret == CIO_OK); + + ret = cio_chunk_up(chunk); + TEST_CHECK(ret == CIO_CORRUPTED); + TEST_CHECK(cio_error_get(chunk) == CIO_ERR_BAD_CHECKSUM); + + cf = (struct cio_file *) chunk->backend; + TEST_CHECK(cf->map == NULL); + TEST_CHECK(cf->fd <= 0); +} + + +void test_fs_up_down_up_append() +{ + int ret; + int err; + struct cio_ctx *ctx; + struct cio_chunk *chunk; + struct cio_stream *stream; + struct cio_options cio_opts; + + void *out_buf; + size_t out_size; + + cio_utils_recursive_delete(CIO_ENV); + + /* create Chunk I/O context */ + cio_options_init(&cio_opts); + + cio_opts.root_path = CIO_ENV; + cio_opts.log_cb = log_cb; + cio_opts.log_level = CIO_LOG_DEBUG; + cio_opts.flags = CIO_CHECKSUM; + + /* Create a temporal storage */ + ctx = cio_create(&cio_opts); + stream = cio_stream_create(ctx, "cio", CIO_STORE_FS); + chunk = cio_chunk_open(ctx, stream, "c", CIO_OPEN, 1000, &err); + TEST_CHECK(chunk != NULL); + if (!chunk) { + printf("cannot open chunk\n"); + exit(1); + } + + ret = cio_chunk_get_content_copy(chunk, &out_buf, &out_size); + TEST_CHECK(ret == CIO_OK); + TEST_CHECK(memcmp(out_buf, "", 1) == 0); + TEST_CHECK(out_size == 0); + free(out_buf); + + ret = cio_chunk_write(chunk, "line 1\n", 7); + TEST_CHECK(ret == CIO_OK); + + ret = cio_chunk_get_content_copy(chunk, &out_buf, &out_size); + TEST_CHECK(ret == CIO_OK); + TEST_CHECK(memcmp(out_buf, "line 1\n", 7+1) == 0); + TEST_CHECK(out_size == 7); + free(out_buf); + + ret = cio_chunk_down(chunk); + TEST_CHECK(ret == CIO_OK); + + ret = cio_chunk_up(chunk); + TEST_CHECK(ret == CIO_OK); + + ret = cio_chunk_get_content_copy(chunk, &out_buf, &out_size); + TEST_CHECK(ret == CIO_OK); + TEST_CHECK(memcmp(out_buf, "line 1\n", 7+1) == 0); + TEST_CHECK(out_size == 7); + free(out_buf); + + /* append */ + ret = cio_chunk_write(chunk, "line 2\n", 7); + TEST_CHECK(ret == CIO_OK); + + ret = cio_chunk_down(chunk); + TEST_CHECK(ret == CIO_OK); + + ret = cio_chunk_up(chunk); + TEST_CHECK(ret == CIO_OK); + + ret = cio_chunk_get_content_copy(chunk, &out_buf, &out_size); + TEST_CHECK(ret == CIO_OK); + TEST_CHECK(memcmp(out_buf, "line 1\nline 2\n", 7*2+1) == 0); + TEST_CHECK(out_size == 7*2); + free(out_buf); + + cio_destroy(ctx); +} + +static void test_deep_hierarchy() +{ + int i; + int ret; + int err; + int len; + char line[] = "this is a test line\n"; + struct cio_ctx *ctx; + struct cio_chunk *chunk; + struct cio_stream *stream; + struct cio_options cio_opts; + + cio_utils_recursive_delete("tmp"); + + /* Create a temporal storage */ + cio_options_init(&cio_opts); + + cio_opts.root_path = "tmp/deep/log/dir"; + cio_opts.log_cb = log_cb; + cio_opts.log_level = CIO_LOG_DEBUG; + cio_opts.flags = 0; + + ctx = cio_create(&cio_opts); + stream = cio_stream_create(ctx, "test", CIO_STORE_FS); + chunk = cio_chunk_open(ctx, stream, "c", CIO_OPEN, 1000, &err); + TEST_CHECK(chunk != NULL); + if (!chunk) { + printf("cannot open chunk\n"); + exit(1); + } + + len = strlen(line); + for (i = 0; i < 1000; i++) { + ret = cio_chunk_write(chunk, line, len); + TEST_CHECK(ret == CIO_OK); + + ret = cio_chunk_down(chunk); + TEST_CHECK(ret == CIO_OK); + + ret = cio_chunk_up(chunk); + TEST_CHECK(ret == CIO_OK); + } + + cio_destroy(ctx); +} + +static void truncate_file(struct cio_file *chunk_file, + size_t new_file_size, + int remove_content_length) +{ + int result; + + result = cio_file_native_open(chunk_file); + TEST_CHECK(result == CIO_OK); + + result = cio_file_native_map(chunk_file, + chunk_file->page_size); + TEST_CHECK(result == CIO_OK); + + if (remove_content_length) { + chunk_file->map[CIO_FILE_CONTENT_LENGTH_OFFSET + 0] = 0; + chunk_file->map[CIO_FILE_CONTENT_LENGTH_OFFSET + 1] = 0; + chunk_file->map[CIO_FILE_CONTENT_LENGTH_OFFSET + 2] = 0; + chunk_file->map[CIO_FILE_CONTENT_LENGTH_OFFSET + 3] = 0; + } + + result = cio_file_native_unmap(chunk_file); + TEST_CHECK(result == CIO_OK); + + result = cio_file_native_resize(chunk_file, new_file_size); + TEST_CHECK(result == 0); + + result = cio_file_native_close(chunk_file); + TEST_CHECK(result == CIO_OK); +} + +static void test_legacy_core(int trigger_checksum_error) +{ + struct cio_options cio_opts; + char *in_data; + size_t in_size; + struct cio_stream *stream; + struct cio_chunk *chunk; + size_t delta; + struct cio_ctx *ctx; + int ret; + + /* delete any previous temporary content directory */ + cio_utils_recursive_delete(CIO_ENV); + /* + * Load sample data file and with the same content through multiple write + * operations generating other files. + */ + ret = cio_utils_read_file(CIO_FILE_400KB, &in_data, &in_size); + TEST_CHECK(ret == 0); + if (ret == -1) { + exit(EXIT_FAILURE); + } + + /* create Chunk I/O context */ + cio_options_init(&cio_opts); + + cio_opts.root_path = CIO_ENV; + cio_opts.log_cb = log_cb; + cio_opts.log_level = CIO_LOG_DEBUG; + cio_opts.flags = CIO_CHECKSUM; + + /* Create a temporal storage */ + ctx = cio_create(&cio_opts); + + stream = cio_stream_create(ctx, "test-legacy", CIO_STORE_FS); + + /* do not force a maximum of chunks up, we want to test writing overhead */ + cio_set_max_chunks_up(ctx, 1); + + chunk = cio_chunk_open(ctx, + stream, + "test_chunk", + CIO_OPEN, + 1000, + &ret); + + ret = cio_chunk_write(chunk, in_data, 128); + TEST_CHECK(ret == 0); + + ret = cio_chunk_down(chunk); + TEST_CHECK(ret == CIO_OK); + + delta = CIO_FILE_HEADER_MIN; + + if (trigger_checksum_error) { + delta++; + } + + truncate_file((struct cio_file *) chunk->backend, + 128 + delta, + CIO_TRUE); + + ret = cio_chunk_up(chunk); + + if (trigger_checksum_error) { + TEST_CHECK(ret != CIO_OK); + } + else { + TEST_CHECK(ret == CIO_OK); + } + + cio_destroy(ctx); + + free(in_data); +} + +void test_legacy_success() +{ + test_legacy_core(CIO_FALSE); +} + +void test_legacy_failure() +{ + test_legacy_core(CIO_TRUE); +} + +TEST_LIST = { + {"fs_write", test_fs_write}, + {"fs_checksum", test_fs_checksum}, + {"fs_up_down", test_fs_up_down}, + {"fs_size_chunks_up", test_fs_size_chunks_up}, + {"issue_51", test_issue_51}, + {"issue_flb_2025", test_issue_flb_2025}, + {"issue_write_at", test_issue_write_at}, + {"fs_up_down_up_append", test_fs_up_down_up_append}, + {"fs_deep_hierachy", test_deep_hierarchy}, + {"legacy_success", test_legacy_success}, + {"legacy_failure", test_legacy_failure}, + { 0 } +}; |