/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* Chunk I/O * ========= * Copyright 2018 Eduardo Silva * * 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 #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #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 } };