summaryrefslogtreecommitdiffstats
path: root/src/fluent-bit/lib/chunkio/tests/fs.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 12:08:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 12:08:18 +0000
commit5da14042f70711ea5cf66e034699730335462f66 (patch)
tree0f6354ccac934ed87a2d555f45be4c831cf92f4a /src/fluent-bit/lib/chunkio/tests/fs.c
parentReleasing debian version 1.44.3-2. (diff)
downloadnetdata-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.c980
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 }
+};