| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * 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. |
| */ |
| |
| #include <assert.h> |
| #include <lk/macros.h> |
| #include <malloc.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <time.h> |
| #include <uapi/err.h> |
| |
| #include <lib/storage/storage.h> |
| |
| #ifndef STORAGE_FAKE |
| #include <lib/system_state/system_state.h> |
| #include <lib/unittest/unittest.h> |
| #include <trusty/time.h> |
| #endif |
| |
| #include <trusty_unittest.h> |
| |
| #define TLOG_TAG "ss_unittest" |
| |
| #if STORAGE_UNITTEST_ON_EMULATOR |
| #define ENABLED_ON_EMULATOR_ONLY(name) name |
| #else |
| #define ENABLED_ON_EMULATOR_ONLY(name) DISABLED_##name |
| #endif |
| |
| typedef void (*test_body)(storage_session_t ss, storage_session_t ss_aux); |
| |
| static const char* storage_test_client_port; |
| static storage_session_t storage_test_ss_persist = STORAGE_INVALID_SESSION; |
| |
| typedef struct { |
| storage_session_t ss; |
| storage_session_t ss_aux; |
| } StorageTest_t; |
| |
| void StorageTest_SetUp(StorageTest_t* state) { |
| state->ss = STORAGE_INVALID_SESSION; |
| state->ss_aux = STORAGE_INVALID_SESSION; |
| |
| if (storage_test_ss_persist != STORAGE_INVALID_SESSION) { |
| storage_close_session(storage_test_ss_persist); |
| storage_test_ss_persist = STORAGE_INVALID_SESSION; |
| } |
| |
| int rc = storage_open_session(&state->ss, storage_test_client_port); |
| if (rc < 0) { |
| TLOGE("failed (%d) to open session\n", rc); |
| return; |
| } |
| |
| #ifndef STORAGE_FAKE |
| rc = storage_open_session(&state->ss_aux, storage_test_client_port); |
| if (rc < 0) { |
| TLOGE("failed (%d) to open session\n", rc); |
| storage_close_session(state->ss); |
| return; |
| } |
| #endif |
| }; |
| |
| void StorageTest_TearDown(StorageTest_t* state) { |
| storage_close_session(state->ss); |
| |
| #ifndef STORAGE_FAKE |
| storage_close_session(state->ss_aux); |
| #endif |
| } |
| |
| #undef TEST_F |
| #define TEST_F(suite_name, test_name) \ |
| TEST_F_CUSTOM_ARGS(suite_name, test_name, (state.ss, state.ss_aux), \ |
| (storage_session_t ss, storage_session_t ss_aux)) |
| |
| #ifndef STORAGE_FAKE |
| TEST_FIXTURE_ALIAS(StorageInitNoCommitSmallTest, StorageTest) |
| TEST_FIXTURE_ALIAS(StorageInitNoCommitLargeTest, StorageTest) |
| TEST_FIXTURE_ALIAS(StorageInitNoCommitCleanupTest, StorageTest) |
| #endif |
| TEST_FIXTURE_ALIAS(StorageInitTest, StorageTest) |
| TEST_FIXTURE_ALIAS(StorageCheckTest, StorageTest) |
| TEST_FIXTURE_ALIAS(StorageCleanTest, StorageTest) |
| |
| static inline bool is_32bit_aligned(size_t sz) { |
| return ((sz & 0x3) == 0); |
| } |
| |
| static inline bool is_valid_size(size_t sz) { |
| return (sz > 0) && is_32bit_aligned(sz); |
| } |
| |
| static bool is_valid_offset(storage_off_t off) { |
| return (off & 0x3) == 0ULL; |
| } |
| |
| static void fill_pattern32(uint32_t* buf, size_t len, storage_off_t off) { |
| size_t cnt = len / sizeof(uint32_t); |
| uint32_t pattern = (uint32_t)(off / sizeof(uint32_t)); |
| for (; cnt > 0; cnt--) { |
| *buf++ = pattern++; |
| } |
| } |
| |
| static bool check_pattern32(const uint32_t* buf, |
| size_t len, |
| storage_off_t off) { |
| size_t cnt = len / sizeof(uint32_t); |
| uint32_t pattern = (uint32_t)(off / sizeof(uint32_t)); |
| for (; cnt > 0; cnt--) { |
| if (*buf != pattern) |
| return false; |
| buf++; |
| pattern++; |
| } |
| return true; |
| } |
| |
| static bool check_value32(const uint32_t* buf, size_t len, uint32_t val) { |
| size_t cnt = len / sizeof(uint32_t); |
| for (; cnt > 0; cnt--) { |
| if (*buf != val) |
| return false; |
| buf++; |
| } |
| return true; |
| } |
| |
| static int WriteZeroChunk(file_handle_t handle, |
| storage_off_t off, |
| size_t chunk_len, |
| uint32_t opflags) { |
| uint32_t data_buf[chunk_len / sizeof(uint32_t)]; |
| |
| assert(is_valid_size(chunk_len)); |
| assert(is_valid_offset(off)); |
| |
| memset(data_buf, 0, chunk_len); |
| |
| return storage_write(handle, off, data_buf, sizeof(data_buf), opflags); |
| } |
| |
| static int WritePatternChunk(file_handle_t handle, |
| storage_off_t off, |
| size_t chunk_len, |
| uint32_t opflags) { |
| uint32_t data_buf[chunk_len / sizeof(uint32_t)]; |
| |
| assert(is_valid_size(chunk_len)); |
| assert(is_valid_offset(off)); |
| |
| fill_pattern32(data_buf, chunk_len, off); |
| |
| return storage_write(handle, off, data_buf, sizeof(data_buf), opflags); |
| } |
| |
| static int WritePatternExt(file_handle_t handle, |
| storage_off_t off, |
| size_t data_len, |
| size_t chunk_len, |
| uint32_t extra_opflags) { |
| size_t written = 0; |
| |
| assert(is_valid_size(data_len)); |
| assert(is_valid_size(chunk_len)); |
| |
| while (data_len) { |
| if (data_len < chunk_len) |
| chunk_len = data_len; |
| int rc = WritePatternChunk(handle, off, chunk_len, |
| (chunk_len == data_len) ? extra_opflags : 0); |
| if (rc < 0) |
| return rc; |
| if ((size_t)rc != chunk_len) |
| return written + rc; |
| off += chunk_len; |
| data_len -= chunk_len; |
| written += chunk_len; |
| } |
| return (int)written; |
| } |
| |
| static int WritePattern(file_handle_t handle, |
| storage_off_t off, |
| size_t data_len, |
| size_t chunk_len, |
| bool complete) { |
| return WritePatternExt(handle, off, data_len, chunk_len, |
| complete ? STORAGE_OP_COMPLETE : 0); |
| } |
| |
| static int ReadChunk(file_handle_t handle, |
| storage_off_t off, |
| size_t chunk_len, |
| size_t head_len, |
| size_t pattern_len, |
| size_t tail_len) { |
| int rc; |
| uint32_t data_buf[chunk_len / sizeof(uint32_t)]; |
| uint8_t* data_ptr = (uint8_t*)data_buf; |
| |
| assert(is_valid_size(chunk_len)); |
| assert(is_valid_offset(off)); |
| assert((head_len + pattern_len + tail_len) == chunk_len); |
| |
| rc = storage_read(handle, off, data_buf, chunk_len); |
| if ((size_t)rc != chunk_len) |
| return rc; |
| |
| if (head_len) { |
| if (!check_value32((const uint32_t*)data_ptr, head_len, 0)) |
| return ERR_CHECKSUM_FAIL; |
| data_ptr += head_len; |
| off += head_len; |
| } |
| |
| if (pattern_len) { |
| if (!check_pattern32((const uint32_t*)data_ptr, pattern_len, off)) |
| return ERR_CHECKSUM_FAIL; |
| data_ptr += pattern_len; |
| } |
| |
| if (tail_len) { |
| if (!check_value32((const uint32_t*)data_ptr, tail_len, 0)) |
| return ERR_CHECKSUM_FAIL; |
| } |
| |
| return chunk_len; |
| } |
| |
| static int ReadPattern(file_handle_t handle, |
| storage_off_t off, |
| size_t data_len, |
| size_t chunk_len) { |
| int rc; |
| size_t bytes_read = 0; |
| uint32_t data_buf[chunk_len / sizeof(uint32_t)]; |
| |
| assert(is_valid_size(chunk_len)); |
| assert(is_valid_size(data_len)); |
| assert(is_valid_offset(off)); |
| |
| while (data_len) { |
| if (chunk_len > data_len) |
| chunk_len = data_len; |
| rc = storage_read(handle, off, data_buf, sizeof(data_buf)); |
| if (rc < 0) |
| return rc; |
| if ((size_t)rc != chunk_len) |
| return bytes_read + rc; |
| if (!check_pattern32(data_buf, chunk_len, off)) |
| return ERR_CHECKSUM_FAIL; |
| off += chunk_len; |
| data_len -= chunk_len; |
| bytes_read += chunk_len; |
| } |
| return bytes_read; |
| } |
| |
| static int ReadPatternEOF(file_handle_t handle, |
| storage_off_t off, |
| size_t chunk_len) { |
| int rc; |
| size_t bytes_read = 0; |
| uint32_t data_buf[chunk_len / sizeof(uint32_t)]; |
| |
| assert(is_valid_size(chunk_len)); |
| |
| while (true) { |
| rc = storage_read(handle, off, data_buf, sizeof(data_buf)); |
| if (rc < 0) |
| return rc; |
| if (rc == 0) |
| break; // end of file reached |
| if (!is_valid_size((size_t)rc)) |
| return ERR_BAD_LEN; |
| if (!check_pattern32(data_buf, rc, off)) |
| return ERR_CHECKSUM_FAIL; |
| off += rc; |
| bytes_read += rc; |
| } |
| return bytes_read; |
| } |
| |
| TEST_F(StorageTest, CreateDelete) { |
| int rc; |
| file_handle_t handle; |
| const char* fname = "test_create_delete_file"; |
| |
| // make sure test file does not exist (expect success or ERR_NOT_FOUND) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| rc = (rc == ERR_NOT_FOUND) ? 0 : rc; |
| ASSERT_EQ(0, rc, "delete test file"); |
| |
| // one more time (expect ERR_NOT_FOUND) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_FOUND, rc, "delete again"); |
| |
| // create file (expect 0) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc, "create test file"); |
| |
| // try to create it again while it is still opened (expect |
| // ERR_ALREADY_EXISTS) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_ALREADY_EXISTS, rc, "create again"); |
| |
| // close it |
| storage_close_file(handle); |
| |
| // try to create it again while it is closed (expect ERR_ALREADY_EXISTS) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_ALREADY_EXISTS, rc, "create again"); |
| |
| // delete file (expect 0) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc, "delete test file"); |
| |
| // one more time (expect ERR_NOT_FOUND) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_FOUND, rc, "delete again"); |
| |
| test_abort:; |
| } |
| |
| #ifndef STORAGE_FAKE |
| // Moving file and opening directory is not supported in fake secure |
| // storage implementation. |
| |
| static int DeleteAllFiles(storage_session_t ss) { |
| int rc; |
| struct storage_open_dir_state* dir; |
| char file_name_dir[64]; |
| uint8_t read_dir_flags; |
| int deleted = 0; |
| |
| // read all test files and delete |
| rc = storage_open_dir(ss, "", &dir); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| while (true) { |
| rc = storage_read_dir(ss, dir, &read_dir_flags, file_name_dir, |
| sizeof(file_name_dir)); |
| if (rc == 0) |
| break; |
| if (rc < 0) { |
| deleted = rc; |
| goto cleanup; |
| } |
| if ((read_dir_flags & STORAGE_FILE_LIST_STATE_MASK) != |
| STORAGE_FILE_LIST_REMOVED) { |
| rc = storage_delete_file(ss, file_name_dir, 0); |
| if (rc != 0) { |
| deleted = rc; |
| goto cleanup; |
| } |
| deleted++; |
| } |
| } |
| |
| rc = storage_end_transaction(ss, true); |
| if (rc != 0) { |
| deleted = rc; |
| goto cleanup; |
| } |
| |
| cleanup: |
| storage_close_dir(ss, dir); |
| |
| return deleted; |
| } |
| |
| TEST_F(StorageTest, ReadAndDelete) { |
| int rc; |
| file_handle_t handle; |
| const char* fname_pat = "test_read_and_delete_%03d_file"; |
| char file_name[64]; |
| struct storage_open_dir_state* dir; |
| char file_name_dir[64]; |
| uint8_t read_dir_flags; |
| int i; |
| int file_count = 100; |
| |
| // clean up dir before the test |
| rc = DeleteAllFiles(ss); |
| ASSERT_LE(0, rc, "cleanup"); |
| |
| // make sure test files do not exist (expect ERR_NOT_FOUND) |
| for (i = 0; i < file_count; i++) { |
| snprintf(file_name, sizeof(file_name), fname_pat, i); |
| rc = storage_delete_file(ss, file_name, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_FOUND, rc, "exists before test"); |
| } |
| |
| // create test files |
| for (i = 0; i < file_count; i++) { |
| snprintf(file_name, sizeof(file_name), fname_pat, i); |
| rc = storage_open_file( |
| ss, &handle, file_name, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc, "create test file"); |
| |
| // close it |
| storage_close_file(handle); |
| } |
| |
| // delete all files by reading the dir |
| rc = DeleteAllFiles(ss); |
| ASSERT_EQ(file_count, rc, "delete_all"); |
| |
| // test empty dir |
| rc = storage_open_dir(ss, "", &dir); |
| ASSERT_EQ(0, rc, "open_dir"); |
| |
| rc = storage_read_dir(ss, dir, &read_dir_flags, file_name_dir, |
| sizeof(file_name_dir)); |
| ASSERT_EQ(0, rc, "read_dir"); |
| ASSERT_EQ(STORAGE_FILE_LIST_END, |
| read_dir_flags & STORAGE_FILE_LIST_STATE_MASK, "dir flags"); |
| |
| storage_close_dir(ss, dir); |
| |
| // make sure test files do not exist (expect ERR_NOT_FOUND) |
| for (i = 0; i < file_count; i++) { |
| snprintf(file_name, sizeof(file_name), fname_pat, i); |
| rc = storage_delete_file(ss, file_name, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_FOUND, rc, "delete after test"); |
| } |
| |
| test_abort: |
| for (i = 0; i < file_count; i++) { |
| snprintf(file_name, sizeof(file_name), fname_pat, i); |
| rc = storage_delete_file(ss, file_name, STORAGE_OP_COMPLETE); |
| } |
| } |
| |
| TEST_F(StorageTest, CreateMoveDelete) { |
| int rc; |
| file_handle_t handle; |
| const char* fname1 = "test_create_move_delete_1_file"; |
| const char* fname2 = "test_create_move_delete_2_file"; |
| |
| // make sure test file does not exist (expect success or ERR_NOT_FOUND) |
| rc = storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); |
| rc = (rc == ERR_NOT_FOUND) ? 0 : rc; |
| ASSERT_EQ(0, rc, "delete test file1"); |
| rc = storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); |
| rc = (rc == ERR_NOT_FOUND) ? 0 : rc; |
| ASSERT_EQ(0, rc, "delete test file2"); |
| |
| // one more time (expect ERR_NOT_FOUND) |
| rc = storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_FOUND, rc, "delete 1 again"); |
| rc = storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_FOUND, rc, "delete 2 again"); |
| |
| // create file (expect 0) |
| rc = storage_open_file( |
| ss, &handle, fname1, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc, "create test file"); |
| |
| // move file |
| rc = storage_move_file(ss, handle, fname1, fname2, |
| STORAGE_FILE_MOVE_CREATE | |
| STORAGE_FILE_MOVE_CREATE_EXCLUSIVE | |
| STORAGE_FILE_MOVE_OPEN_FILE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc, "move test file"); |
| |
| // try to create it again while it is still opened (expect |
| // ERR_ALREADY_EXISTS) |
| rc = storage_open_file( |
| ss, &handle, fname2, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_ALREADY_EXISTS, rc, "create again"); |
| |
| // close it |
| storage_close_file(handle); |
| |
| // try to create it again while it is closed (expect ERR_ALREADY_EXISTS) |
| rc = storage_open_file( |
| ss, &handle, fname2, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_ALREADY_EXISTS, rc, "create again"); |
| |
| // create file1 (expect 0) |
| rc = storage_open_file( |
| ss, &handle, fname1, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc, "create test file"); |
| |
| // move file |
| rc = storage_move_file(ss, handle, fname1, fname2, |
| STORAGE_FILE_MOVE_CREATE | |
| STORAGE_FILE_MOVE_CREATE_EXCLUSIVE | |
| STORAGE_FILE_MOVE_OPEN_FILE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_ALREADY_EXISTS, rc, "move test file"); |
| |
| // close it |
| storage_close_file(handle); |
| |
| // delete file (expect 0) |
| rc = storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc, "delete test file"); |
| |
| // one more time (expect ERR_NOT_FOUND) |
| rc = storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_FOUND, rc, "delete again"); |
| |
| test_abort: |
| storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); |
| storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); |
| } |
| |
| TEST_F(StorageTest, FileList) { |
| int rc; |
| file_handle_t handle; |
| struct storage_open_dir_state* dir; |
| const char* fname_pat = "test_file_list_%d_file"; |
| char file_name[64]; |
| char file_name_dir[64]; |
| uint8_t read_dir_flags; |
| int i; |
| int file_count = 100; |
| |
| // clean up dir before the test |
| rc = DeleteAllFiles(ss); |
| ASSERT_LE(0, rc, "cleanup"); |
| |
| // make sure test files do not exist (expect ERR_NOT_FOUND) |
| for (i = 0; i < file_count; i++) { |
| snprintf(file_name, sizeof(file_name), fname_pat, i); |
| rc = storage_delete_file(ss, file_name, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_FOUND, rc, "delete 1 again"); |
| } |
| |
| // test empty dir |
| rc = storage_open_dir(ss, "", &dir); |
| ASSERT_EQ(0, rc, "open_dir"); |
| |
| rc = storage_read_dir(ss, dir, &read_dir_flags, file_name_dir, |
| sizeof(file_name_dir)); |
| ASSERT_EQ(0, rc, "read_dir"); |
| ASSERT_EQ(STORAGE_FILE_LIST_END, |
| read_dir_flags & STORAGE_FILE_LIST_STATE_MASK, "dir flags"); |
| |
| storage_close_dir(ss, dir); |
| |
| // create file (expect 0) |
| snprintf(file_name, sizeof(file_name), fname_pat, 0); |
| rc = storage_open_file( |
| ss, &handle, file_name, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc, "create test file 1"); |
| |
| // close it |
| storage_close_file(handle); |
| |
| for (i = 1; i < file_count; i++) { |
| snprintf(file_name, sizeof(file_name), fname_pat, i); |
| rc = storage_open_file( |
| ss, &handle, file_name, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, |
| 0); |
| ASSERT_EQ(0, rc, "create test file 2"); |
| |
| // close it |
| storage_close_file(handle); |
| } |
| |
| // test read_dir fname1 committed, fname2 added |
| rc = storage_open_dir(ss, "", &dir); |
| ASSERT_EQ(0, rc, "open_dir"); |
| |
| snprintf(file_name, sizeof(file_name), fname_pat, 0); |
| rc = storage_read_dir(ss, dir, &read_dir_flags, file_name_dir, |
| sizeof(file_name_dir)); |
| ASSERT_EQ(strlen(file_name) + 1, rc, "read_dir"); |
| ASSERT_EQ(STORAGE_FILE_LIST_COMMITTED, |
| read_dir_flags & STORAGE_FILE_LIST_STATE_MASK, "dir flags"); |
| ASSERT_EQ(0, strcmp(file_name, file_name_dir), "file name"); |
| |
| for (i = 1; i < file_count; i++) { |
| rc = storage_read_dir(ss, dir, &read_dir_flags, file_name_dir, |
| sizeof(file_name_dir)); |
| ASSERT_EQ(strlen(file_name_dir) + 1, rc, "read_dir"); |
| ASSERT_EQ(STORAGE_FILE_LIST_ADDED, |
| read_dir_flags & STORAGE_FILE_LIST_STATE_MASK, "dir flags"); |
| ASSERT_NE(0, strcmp(file_name, file_name_dir), "file name"); |
| } |
| |
| rc = storage_read_dir(ss, dir, &read_dir_flags, file_name_dir, |
| sizeof(file_name_dir)); |
| ASSERT_EQ(0, rc, "read_dir"); |
| ASSERT_EQ(STORAGE_FILE_LIST_END, |
| read_dir_flags & STORAGE_FILE_LIST_STATE_MASK, "end dir flag"); |
| |
| storage_close_dir(ss, dir); |
| |
| rc = storage_end_transaction(ss, true); |
| ASSERT_EQ(0, rc, "commit"); |
| |
| test_abort: |
| for (i = 0; i < file_count; i++) { |
| snprintf(file_name, sizeof(file_name), fname_pat, i); |
| rc = storage_delete_file(ss, file_name, STORAGE_OP_COMPLETE); |
| } |
| } |
| #endif |
| |
| TEST_F(StorageTest, DeleteOpened) { |
| int rc; |
| file_handle_t handle; |
| const char* fname = "delete_opened_test_file"; |
| |
| // make sure test file does not exist (expect success or ERR_NOT_FOUND) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| rc = (rc == ERR_NOT_FOUND) ? 0 : rc; |
| ASSERT_EQ(0, rc); |
| |
| // one more time (expect ERR_NOT_FOUND) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| // open/create file (expect 0) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // delete opened file (expect 0) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // one more time (expect ERR_NOT_FOUND) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| // close file |
| storage_close_file(handle); |
| |
| // one more time (expect ERR_NOT_FOUND) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, OpenNoCreate) { |
| int rc; |
| file_handle_t handle; |
| const char* fname = "test_open_no_create_file"; |
| |
| // make sure test file does not exist (expect success or ERR_NOT_FOUND) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| rc = (rc == ERR_NOT_FOUND) ? 0 : rc; |
| ASSERT_EQ(0, rc); |
| |
| // open non-existing file (expect ERR_NOT_FOUND) |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| // create file (expect 0) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| storage_close_file(handle); |
| |
| // open existing file (expect 0) |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // close it |
| storage_close_file(handle); |
| |
| // delete file (expect 0) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, OpenOrCreate) { |
| int rc; |
| file_handle_t handle; |
| const char* fname = "test_open_create_file"; |
| |
| // make sure test file does not exist (expect success or ERR_NOT_FOUND) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| rc = (rc == ERR_NOT_FOUND) ? 0 : rc; |
| ASSERT_EQ(0, rc); |
| |
| // open/create a non-existing file (expect 0) |
| rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_CREATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| storage_close_file(handle); |
| |
| // open/create an existing file (expect 0) |
| rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_CREATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| storage_close_file(handle); |
| |
| // delete file (expect 0) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, OpenCreateDeleteCharset) { |
| int rc; |
| file_handle_t handle; |
| const char* fname = |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz_01234.56789"; |
| |
| // open/create file (expect 0) |
| rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_CREATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| storage_close_file(handle); |
| |
| // open/create an existing file (expect 0) |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| storage_close_file(handle); |
| |
| // delete file (expect 0) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // open again |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, WriteReadSequential) { |
| int rc; |
| size_t blk = 2048; |
| file_handle_t handle; |
| const char* fname = "test_write_read_sequential"; |
| |
| // make sure test file does not exist (expect success or ERR_NOT_FOUND) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| rc = (rc == ERR_NOT_FOUND) ? 0 : rc; |
| ASSERT_EQ(0, rc); |
| |
| // create file. |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // write a bunch of blocks (sequentially) |
| rc = WritePattern(handle, 0, 32 * blk, blk, true); |
| ASSERT_EQ((int)(32 * blk), rc); |
| |
| rc = ReadPattern(handle, 0, 32 * blk, blk); |
| ASSERT_EQ((int)(32 * blk), rc); |
| |
| // close file |
| storage_close_file(handle); |
| |
| // open the same file again |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // read data back (sequentially) and check pattern again |
| rc = ReadPattern(handle, 0, 32 * blk, blk); |
| ASSERT_EQ((int)(32 * blk), rc); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, OpenTruncate) { |
| int rc; |
| uint32_t val; |
| size_t blk = 2048; |
| file_handle_t handle; |
| const char* fname = "test_open_truncate"; |
| |
| // make sure test file does not exist (expect success or ERR_NOT_FOUND) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| rc = (rc == ERR_NOT_FOUND) ? 0 : rc; |
| ASSERT_EQ(0, rc); |
| |
| // create file. |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // write some data and read it back |
| rc = WritePatternChunk(handle, 0, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| |
| rc = ReadPattern(handle, 0, blk, blk); |
| ASSERT_EQ((int)blk, rc); |
| |
| // close file |
| storage_close_file(handle); |
| |
| // reopen with truncate |
| rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| /* try to read data back (expect no data) */ |
| rc = storage_read(handle, 0LL, &val, sizeof(val)); |
| ASSERT_EQ(0, rc); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, OpenSame) { |
| int rc; |
| file_handle_t handle1; |
| file_handle_t handle2; |
| file_handle_t handle3; |
| const char* fname = "test_open_same_file"; |
| |
| // open/create file (expect 0) |
| rc = storage_open_file(ss, &handle1, fname, STORAGE_FILE_OPEN_CREATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| storage_close_file(handle1); |
| |
| // open an existing file first time (expect 0) |
| rc = storage_open_file(ss, &handle1, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // open the same file second time (is not allowed) |
| rc = storage_open_file(ss, &handle2, fname, 0, 0); |
| ASSERT_EQ(ERR_NOT_ALLOWED, rc); |
| |
| // delete file (expect 0) |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // open deleted file (expect ERR_NOT_FOUND) |
| rc = storage_open_file(ss, &handle3, fname, 0, 0); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| // close file |
| storage_close_file(handle1); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, OpenMany) { |
| int rc; |
| file_handle_t handles[10]; |
| char filename[10]; |
| const char* fname_fmt = "mf%d"; |
| |
| // open or create a bunch of files (expect 0) |
| for (uint32_t i = 0; i < countof(handles); ++i) { |
| snprintf(filename, sizeof(filename), fname_fmt, i); |
| rc = storage_open_file(ss, &handles[i], filename, |
| STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| } |
| |
| // check that all handles are different |
| for (uint32_t i = 0; i < countof(handles) - 1; i++) { |
| for (uint32_t j = i + 1; j < countof(handles); j++) { |
| ASSERT_NE(handles[i], handles[j]); |
| } |
| } |
| |
| // close them all |
| for (uint32_t i = 0; i < countof(handles); ++i) { |
| storage_close_file(handles[i]); |
| } |
| |
| // open all files without CREATE flags (expect 0) |
| for (uint32_t i = 0; i < countof(handles); ++i) { |
| snprintf(filename, sizeof(filename), fname_fmt, i); |
| rc = storage_open_file(ss, &handles[i], filename, 0, 0); |
| ASSERT_EQ(0, rc); |
| } |
| |
| // check that all handles are different |
| for (uint32_t i = 0; i < countof(handles) - 1; i++) { |
| for (uint32_t j = i + 1; j < countof(handles); j++) { |
| ASSERT_NE(handles[i], handles[j]); |
| } |
| } |
| |
| // close and remove all test files |
| for (uint32_t i = 0; i < countof(handles); ++i) { |
| storage_close_file(handles[i]); |
| snprintf(filename, sizeof(filename), fname_fmt, i); |
| rc = storage_delete_file(ss, filename, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| } |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, ReadAtEOF) { |
| int rc; |
| uint32_t val; |
| size_t blk = 2048; |
| file_handle_t handle; |
| const char* fname = "test_read_eof"; |
| |
| // open/create/truncate file |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // write block at offset 0 |
| rc = WritePatternChunk(handle, 0, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| |
| // close file |
| storage_close_file(handle); |
| |
| // open same file again |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // read the whole block back and check pattern again |
| rc = ReadPattern(handle, 0, blk, blk); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read at end of file (expected 0 bytes) |
| rc = storage_read(handle, blk, &val, sizeof(val)); |
| ASSERT_EQ(0, rc); |
| |
| // partial read at end of the file (expected partial data) |
| rc = ReadPatternEOF(handle, blk / 2, blk); |
| ASSERT_EQ((int)blk / 2, rc); |
| |
| // read past end of file |
| rc = storage_read(handle, blk + 2, &val, sizeof(val)); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, GetFileSize) { |
| int rc; |
| size_t blk = 2048; |
| storage_off_t size; |
| file_handle_t handle; |
| const char* fname = "test_get_file_size"; |
| |
| // open/create/truncate file. |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // check file size (expect success and size == 0) |
| size = 1; |
| rc = storage_get_file_size(handle, &size); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, size); |
| |
| // write block |
| rc = WritePatternChunk(handle, 0, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| |
| // check size |
| rc = storage_get_file_size(handle, &size); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ(blk, size); |
| |
| // write another block |
| rc = WritePatternChunk(handle, blk, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| |
| // check size again |
| rc = storage_get_file_size(handle, &size); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ(blk * 2, size); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, SetFileSize) { |
| int rc; |
| size_t blk = 2048; |
| storage_off_t size; |
| file_handle_t handle; |
| const char* fname = "test_set_file_size"; |
| |
| // open/create/truncate file. |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // check file size (expect success and size == 0) |
| size = 1; |
| rc = storage_get_file_size(handle, &size); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, size); |
| |
| // write block |
| rc = WritePatternChunk(handle, 0, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| |
| // check size |
| rc = storage_get_file_size(handle, &size); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ(blk, size); |
| |
| storage_close_file(handle); |
| |
| // reopen normally |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // check size again |
| rc = storage_get_file_size(handle, &size); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ(blk, size); |
| |
| // set file size to half |
| rc = storage_set_file_size(handle, blk / 2, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // check size again (should be half of original size) |
| rc = storage_get_file_size(handle, &size); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ(blk / 2, size); |
| |
| // read data back |
| rc = ReadPatternEOF(handle, 0, blk); |
| ASSERT_EQ((int)blk / 2, rc); |
| |
| // set file size to 0 |
| rc = storage_set_file_size(handle, 0, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // check size again (should be 0) |
| rc = storage_get_file_size(handle, &size); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0LL, size); |
| |
| // try to read again |
| rc = ReadPatternEOF(handle, 0, blk); |
| ASSERT_EQ(0, rc); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, WriteReadAtOffset) { |
| int rc; |
| file_handle_t handle; |
| size_t blk = 2048; |
| size_t blk_cnt = 32; |
| const char* fname = "test_write_at_offset"; |
| |
| // create/truncate file. |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // write a bunch of blocks filled with zeroes |
| for (size_t i = 0; i < blk_cnt; i++) { |
| rc = WriteZeroChunk(handle, i * blk, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| } |
| |
| storage_off_t off1 = blk; |
| storage_off_t off2 = blk * (blk_cnt - 1); |
| |
| // write known pattern data at non-zero offset1 |
| rc = WritePatternChunk(handle, off1, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| |
| // write known pattern data at non-zero offset2 |
| rc = WritePatternChunk(handle, off2, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read data back at offset1 |
| rc = ReadPattern(handle, off1, blk, blk); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read data back at offset2 |
| rc = ReadPattern(handle, off2, blk, blk); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read partially written data at end of file(expect to get data only, no |
| // padding) |
| rc = ReadPatternEOF(handle, off2 + blk / 2, blk); |
| ASSERT_EQ((int)blk / 2, rc); |
| |
| // read data at offset 0 (expect success and zero data) |
| rc = ReadChunk(handle, 0, blk, blk, 0, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read data from gap (expect success and zero data) |
| rc = ReadChunk(handle, off1 + blk, blk, blk, 0, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read partially written data (start pointing within written data) |
| // (expect to get written data back and zeroes at the end) |
| rc = ReadChunk(handle, off1 + blk / 2, blk, 0, blk / 2, blk / 2); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read partially written data (start pointing within unwritten data) |
| // expect to get zeroes at the beginning and proper data at the end |
| rc = ReadChunk(handle, off1 - blk / 2, blk, blk / 2, blk / 2, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, WriteReadAtOffsetSparse) { |
| int rc; |
| file_handle_t handle; |
| size_t blk = 2048; |
| size_t blk_cnt = 32; |
| const char* fname = "test_write_at_offset"; |
| |
| // create/truncate file. |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| storage_off_t off1 = blk; |
| storage_off_t off2 = blk * (blk_cnt - 1); |
| |
| // write known pattern data at non-zero offset1 |
| rc = WritePatternChunk(handle, off1, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| |
| // write known pattern data at non-zero offset2 |
| rc = WritePatternChunk(handle, off2, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read data back at offset1 |
| rc = ReadPattern(handle, off1, blk, blk); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read data back at offset2 |
| rc = ReadPattern(handle, off2, blk, blk); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read partially written data at end of file(expect to get data only, no |
| // padding) |
| rc = ReadPatternEOF(handle, off2 + blk / 2, blk); |
| ASSERT_EQ((int)blk / 2, rc); |
| |
| // read data at offset 0 (expect success and zero data) |
| rc = ReadChunk(handle, 0, blk, blk, 0, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read data from gap (expect success and zero data) |
| rc = ReadChunk(handle, off1 + blk, blk, blk, 0, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read partially written data (start pointing within written data) |
| // (expect to get written data back and zeroes at the end) |
| rc = ReadChunk(handle, off1 + blk / 2, blk, 0, blk / 2, blk / 2); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read partially written data (start pointing within unwritten data) |
| // expect to get zeroes at the beginning and proper data at the end |
| rc = ReadChunk(handle, off1 - blk / 2, blk, blk / 2, blk / 2, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // set file size to half way into first written block |
| rc = storage_set_file_size(handle, off1 + blk / 2, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // read partially written data at end of file(expect to get data only, no |
| // padding) |
| rc = ReadPatternEOF(handle, off1, blk); |
| ASSERT_EQ((int)blk / 2, rc); |
| |
| // write known pattern data at non-zero offset2 |
| rc = WritePatternChunk(handle, off2, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read data back at offset1 |
| rc = ReadPattern(handle, off1, blk / 2, blk / 2); |
| ASSERT_EQ((int)blk / 2, rc); |
| |
| // read data from gap (expect success and zero data) |
| rc = ReadChunk(handle, off1 + blk / 2, blk, blk, 0, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // write known pattern data at non-zero offset1 - test again with |
| // set_file_size |
| rc = WritePatternChunk(handle, off1, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| |
| // set file size to half way into first written block |
| rc = storage_set_file_size(handle, off1 + blk / 2, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // read partially written data at end of file(expect to get data only, no |
| // padding) |
| rc = ReadPatternEOF(handle, off1, blk); |
| ASSERT_EQ((int)blk / 2, rc); |
| |
| // set file size to offset2 |
| rc = storage_set_file_size(handle, off2, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // write known pattern data at non-zero offset2 |
| rc = WritePatternChunk(handle, off2, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read data back at offset1 |
| rc = ReadPattern(handle, off1, blk / 2, blk / 2); |
| ASSERT_EQ((int)blk / 2, rc); |
| |
| // read data from gap (expect success and zero data) |
| rc = ReadChunk(handle, off1 + blk / 2, blk, blk, 0, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| #ifndef STORAGE_FAKE |
| // STORAGE_FAKE does not support multiple sessionsa |
| |
| // Leave a small transaction open with uncommitted data blocks. This test should |
| // fit in the cache so no data should need to be written to disk. This can be |
| // used on a newly wiped filesystem to test the cleanup path if storageproxyd |
| // disconnects before anything has been written. |
| TEST_F(StorageInitNoCommitSmallTest, CreatePersistentNoCommitSmall) { |
| int rc; |
| file_handle_t handle; |
| const char* fname = "test_persistent_small_uncommited_file"; |
| |
| rc = storage_open_session(&storage_test_ss_persist, |
| storage_test_client_port); |
| ASSERT_EQ(0, rc); |
| |
| // create/truncate file. |
| rc = storage_open_file( |
| storage_test_ss_persist, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, 0); |
| ASSERT_EQ(0, rc); |
| |
| // close but do not delete file |
| storage_close_file(handle); |
| |
| test_abort:; |
| } |
| |
| // Leave large a transaction open with uncommitted data blocks. This can be used |
| // on a newly wiped filesystem to leave it in a state where only data blocks |
| // have been written and expose bugs in how we auto clear the td partition. |
| TEST_F(StorageInitNoCommitLargeTest, CreatePersistentNoCommitLarge) { |
| int rc; |
| file_handle_t handle; |
| size_t blk = 2048; |
| // Size of file should be too large to fit in block cache |
| size_t file_size = 64 * 2048; |
| const char* fname = "test_persistent_large_uncommited_file"; |
| |
| rc = storage_open_session(&storage_test_ss_persist, |
| storage_test_client_port); |
| ASSERT_EQ(0, rc); |
| |
| // create/truncate file. |
| rc = storage_open_file( |
| storage_test_ss_persist, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, 0); |
| ASSERT_EQ(0, rc); |
| |
| // write a bunch of blocks filled with pattern |
| rc = WritePattern(handle, 0, file_size, blk, false); |
| ASSERT_EQ((int)file_size, rc); |
| |
| // close but do not delete file |
| storage_close_file(handle); |
| |
| test_abort:; |
| } |
| |
| // Empty test that can be used to close the session created by |
| // CreatePersistentNoCommitSmall or CreatePersistentNoCommitLarge. |
| TEST_F(StorageInitNoCommitCleanupTest, NoCommitCleanup) { |
| // Fixture setup function closes the previous persistent session. |
| } |
| #endif |
| |
| TEST_F(StorageInitTest, CreatePersistent32K) { |
| int rc; |
| file_handle_t handle; |
| size_t blk = 2048; |
| size_t file_size = 32768; |
| const char* fname = "test_persistent_32K_file"; |
| |
| // create/truncate file. Don't commit until the write is complete so we |
| // only perform a single superblock update |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, 0); |
| ASSERT_EQ(0, rc); |
| |
| // write a bunch of blocks filled with pattern |
| rc = WritePattern(handle, 0, file_size, blk, true); |
| ASSERT_EQ((int)file_size, rc); |
| |
| // close but do not delete file |
| storage_close_file(handle); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageCheckTest, ReadPersistent32k) { |
| int rc; |
| file_handle_t handle; |
| size_t exp_len = 32 * 1024; |
| const char* fname = "test_persistent_32K_file"; |
| |
| // create/truncate file. |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| rc = ReadPatternEOF(handle, 0, 2048); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| rc = ReadPatternEOF(handle, 0, 1024); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| rc = ReadPatternEOF(handle, 0, 332); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| // close but do not delete file |
| storage_close_file(handle); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageCleanTest, CleanUpPersistent32K) { |
| int rc; |
| const char* fname = "test_persistent_32K_file"; |
| |
| rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| rc = (rc == ERR_NOT_FOUND) ? 0 : rc; |
| ASSERT_EQ(0, rc); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, WriteReadLong) { |
| int rc; |
| file_handle_t handle; |
| size_t wc = 10000; |
| const char* fname = "test_write_read_long"; |
| |
| uint32_t* test_buf_ = malloc(wc * sizeof(uint32_t)); |
| ASSERT_NE(NULL, test_buf_); |
| |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| fill_pattern32(test_buf_, wc * sizeof(uint32_t), 0); |
| rc = storage_write(handle, 0, test_buf_, wc * sizeof(uint32_t), |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ((int)(wc * sizeof(uint32_t)), rc); |
| |
| rc = storage_read(handle, 0, test_buf_, wc * sizeof(uint32_t)); |
| ASSERT_EQ((int)(wc * sizeof(uint32_t)), rc); |
| |
| bool res = check_pattern32(test_buf_, wc * sizeof(uint32_t), 0); |
| ASSERT_EQ(true, res); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort: |
| if (test_buf_) |
| free(test_buf_); |
| } |
| |
| // Negative tests |
| |
| TEST_F(StorageTest, OpenInvalidFileName) { |
| int rc; |
| file_handle_t handle; |
| const char* fname1 = ""; |
| const char* fname2 = "ffff$ffff"; |
| const char* fname3 = "ffff\\ffff"; |
| char max_name[STORAGE_MAX_NAME_LENGTH_BYTES + 1]; |
| |
| rc = storage_open_file( |
| ss, &handle, fname1, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| rc = storage_open_file( |
| ss, &handle, fname2, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| rc = storage_open_file( |
| ss, &handle, fname3, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| /* max name */ |
| memset(max_name, 'a', sizeof(max_name)); |
| max_name[sizeof(max_name) - 1] = 0; |
| |
| rc = storage_open_file( |
| ss, &handle, max_name, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| max_name[sizeof(max_name) - 2] = 0; |
| rc = storage_open_file( |
| ss, &handle, max_name, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| storage_close_file(handle); |
| storage_delete_file(ss, max_name, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, BadFileHandle) { |
| int rc; |
| file_handle_t handle; |
| const char* fname = "test_invalid_file_handle"; |
| |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // write to invalid file handle |
| uint32_t val = 0xDEDBEEF; |
| rc = storage_write(handle + 1, 0, &val, sizeof(val), STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| // read from invalid handle |
| rc = storage_read(handle + 1, 0, &val, sizeof(val)); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| // set size |
| rc = storage_set_file_size(handle + 1, 0, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| // get size |
| storage_off_t fsize = (storage_off_t)(-1); |
| rc = storage_get_file_size(handle + 1, &fsize); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| // close (there is no way to check errors here) |
| storage_close_file(handle + 1); |
| |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, ClosedFileHandle) { |
| int rc; |
| file_handle_t handle1; |
| file_handle_t handle2; |
| const char* fname1 = "test_invalid_file_handle1"; |
| const char* fname2 = "test_invalid_file_handle2"; |
| |
| rc = storage_open_file( |
| ss, &handle1, fname1, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| rc = storage_open_file( |
| ss, &handle2, fname2, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // close first file handle |
| storage_close_file(handle1); |
| |
| // write to invalid file handle |
| uint32_t val = 0xDEDBEEF; |
| rc = storage_write(handle1, 0, &val, sizeof(val), STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| // read from invalid handle |
| rc = storage_read(handle1, 0, &val, sizeof(val)); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| // set size |
| rc = storage_set_file_size(handle1, 0, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| // get size |
| storage_off_t fsize = (storage_off_t)(-1); |
| rc = storage_get_file_size(handle1, &fsize); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| // close (there is no way to check errors here) |
| storage_close_file(handle1); |
| |
| // clean up |
| storage_close_file(handle2); |
| storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); |
| storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| #ifndef STORAGE_FAKE |
| TEST_F(StorageTest, TransactDiscardInactive) { |
| int rc; |
| |
| // discard current transaction (there should not be any) |
| rc = storage_end_transaction(ss, false); |
| ASSERT_EQ(0, rc); |
| |
| // try it again |
| rc = storage_end_transaction(ss, false); |
| ASSERT_EQ(0, rc); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactCommitInactive) { |
| int rc; |
| |
| // try to commit current transaction |
| rc = storage_end_transaction(ss, true); |
| ASSERT_EQ(0, rc); |
| |
| // try it again |
| rc = storage_end_transaction(ss, true); |
| ASSERT_EQ(0, rc); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactDiscardWrite) { |
| int rc; |
| file_handle_t handle; |
| size_t blk = 2048; |
| size_t exp_len = 32 * 1024; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname = "test_transact_discard_write"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| // write (without commit) |
| rc = WritePattern(handle, 0, exp_len, blk, false); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // abort current transaction |
| rc = storage_end_transaction(ss, false); |
| ASSERT_EQ(0, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactDiscardWriteAppend) { |
| int rc; |
| file_handle_t handle; |
| size_t blk = 2048; |
| size_t exp_len = 32 * 1024; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname = "test_transact_write_append"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // write data with commit |
| rc = WritePattern(handle, 0, exp_len / 2, blk, true); |
| ASSERT_EQ((int)exp_len / 2, rc); |
| |
| // write data without commit |
| rc = WritePattern(handle, exp_len / 2, exp_len / 2, blk, false); |
| ASSERT_EQ((int)exp_len / 2, rc); |
| |
| // check file size (should be exp_len) |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // discard transaction |
| rc = storage_end_transaction(ss, false); |
| ASSERT_EQ(0, rc); |
| |
| // check file size, it should be exp_len/2 |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len / 2, fsize); |
| |
| // check file data |
| rc = ReadPatternEOF(handle, 0, blk); |
| ASSERT_EQ((int)exp_len / 2, rc); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactDiscardWriteRead) { |
| int rc; |
| file_handle_t handle; |
| size_t blk = 2048; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname = "test_transact_discard_write_read"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| // Fill with zeroes (with commit) |
| for (uint32_t i = 0; i < 32; i++) { |
| rc = WriteZeroChunk(handle, i * blk, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| } |
| |
| // check that test chunk is filled with zeroes |
| rc = ReadChunk(handle, blk, blk, blk, 0, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // write test pattern (without commit) |
| rc = WritePattern(handle, blk, blk, blk, false); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read it back an check pattern |
| rc = ReadChunk(handle, blk, blk, 0, blk, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // abort current transaction |
| rc = storage_end_transaction(ss, false); |
| ASSERT_EQ(0, rc); |
| |
| // read same chunk back (should be filled with zeros) |
| rc = ReadChunk(handle, blk, blk, blk, 0, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactDiscardWriteMany) { |
| int rc; |
| file_handle_t handle1; |
| file_handle_t handle2; |
| size_t blk = 2048; |
| size_t exp_len1 = 32 * 1024; |
| size_t exp_len2 = 31 * 1024; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname1 = "test_transact_discard_write_file1"; |
| const char* fname2 = "test_transact_discard_write_file2"; |
| |
| // open create truncate (with commit) |
| rc = storage_open_file( |
| ss, &handle1, fname1, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // open create truncate (with commit) |
| rc = storage_open_file( |
| ss, &handle2, fname2, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // file1: fill file with pattern (without commit) |
| rc = WritePattern(handle1, 0, exp_len1, blk, false); |
| ASSERT_EQ((int)exp_len1, rc); |
| |
| // file2: fill file with pattern (without commit) |
| rc = WritePattern(handle2, 0, exp_len2, blk, false); |
| ASSERT_EQ((int)exp_len2, rc); |
| |
| // check file size, it should be exp_len1 |
| rc = storage_get_file_size(handle1, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len1, fsize); |
| |
| // check file size, it should be exp_len2 |
| rc = storage_get_file_size(handle2, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len2, fsize); |
| |
| // discard transaction |
| rc = storage_end_transaction(ss, false); |
| ASSERT_EQ(0, rc); |
| |
| // check file size, it should be 0 |
| rc = storage_get_file_size(handle1, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| // check file size, it should be 0 |
| rc = storage_get_file_size(handle2, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| // check data |
| rc = ReadPatternEOF(handle1, 0, blk); |
| ASSERT_EQ(0, rc); |
| |
| rc = ReadPatternEOF(handle2, 0, blk); |
| ASSERT_EQ(0, rc); |
| |
| // cleanup |
| storage_close_file(handle1); |
| storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); |
| storage_close_file(handle2); |
| storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactDiscardTruncate) { |
| int rc; |
| file_handle_t handle; |
| size_t blk = 2048; |
| size_t exp_len = 32 * 1024; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname = "test_transact_discard_truncate"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // write data (with commit) |
| rc = WritePattern(handle, 0, exp_len, blk, true); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // close file |
| storage_close_file(handle); |
| |
| // open truncate file (without commit) |
| rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0); |
| ASSERT_EQ(0, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| // abort current transaction |
| rc = storage_end_transaction(ss, false); |
| ASSERT_EQ(0, rc); |
| |
| // check file size (should be an oruginal size) |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactDiscardSetSize) { |
| int rc; |
| file_handle_t handle; |
| size_t blk = 2048; |
| size_t exp_len = 32 * 1024; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname = "test_transact_discard_set_size"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // write data (with commit) |
| rc = WritePattern(handle, 0, exp_len, blk, true); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // set file size to half of original (no commit) |
| rc = storage_set_file_size(handle, (storage_off_t)exp_len / 2, 0); |
| ASSERT_EQ(0, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len / 2, fsize); |
| |
| // set file size to 1/3 of original (no commit) |
| rc = storage_set_file_size(handle, (storage_off_t)exp_len / 3, 0); |
| ASSERT_EQ(0, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len / 3, fsize); |
| |
| // abort current transaction |
| rc = storage_end_transaction(ss, false); |
| ASSERT_EQ(0, rc); |
| |
| // check file size (should be an original size) |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactDiscardDelete) { |
| int rc; |
| file_handle_t handle; |
| size_t blk = 2048; |
| size_t exp_len = 32 * 1024; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname = "test_transact_discard_delete"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // write data (with commit) |
| rc = WritePattern(handle, 0, exp_len, blk, true); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| // close it |
| storage_close_file(handle); |
| |
| // delete file (without commit) |
| rc = storage_delete_file(ss, fname, 0); |
| ASSERT_EQ(0, rc); |
| |
| // try to open it (should fail) |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| // abort current transaction |
| rc = storage_end_transaction(ss, false); |
| ASSERT_EQ(0, rc); |
| |
| // try to open it |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // check file size (should be an original size) |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactDiscardCreate) { |
| int rc; |
| file_handle_t handle; |
| const char* fname = "test_transact_discard_create_excl"; |
| |
| // delete test file just in case |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| // create file (without commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, 0); |
| ASSERT_EQ(0, rc); |
| |
| // close it |
| storage_close_file(handle); |
| |
| // open it again without create and without commit (expect success) |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // close it |
| storage_close_file(handle); |
| |
| // abort current transaction |
| rc = storage_end_transaction(ss, false); |
| ASSERT_EQ(0, rc); |
| |
| // open it again without create without commit (expect not found) |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| // cleanup |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactCommitWrites) { |
| int rc; |
| file_handle_t handle; |
| file_handle_t handle_aux; |
| size_t blk = 2048; |
| size_t exp_len = 32 * 1024; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname = "test_transact_commit_writes"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // open the same file in aux session |
| rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // check file size, it should be 0 |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| // write data in primary session (without commit) |
| rc = WritePattern(handle, 0, exp_len / 2, blk, false); |
| ASSERT_EQ((int)exp_len / 2, rc); |
| |
| // write more data in primary (without commit) |
| rc = WritePattern(handle, exp_len / 2, exp_len / 2, blk, false); |
| ASSERT_EQ((int)exp_len / 2, rc); |
| |
| // check file size in aux session, it should still be 0 |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| // commit current transaction |
| rc = storage_end_transaction(ss, true); |
| ASSERT_EQ(0, rc); |
| |
| // check file size of aux session, should fail |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(ERR_BUSY, rc); |
| |
| // abort transaction in aux session to recover |
| rc = storage_end_transaction(ss_aux, false); |
| ASSERT_EQ(0, rc); |
| |
| // check file size in aux session, it should be exp_len |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // check file size in primary session, it should be exp_len |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // check data in primary session |
| rc = ReadPatternEOF(handle, 0, blk); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| // check data in aux session |
| rc = ReadPatternEOF(handle_aux, 0, blk); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_close_file(handle_aux); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactCommitWrites2) { |
| int rc; |
| file_handle_t handle; |
| file_handle_t handle_aux; |
| size_t blk = 2048; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname = "test_transact_commit_writes2"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // open the same file in separate session |
| rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| // discard transaction in aux_session |
| rc = storage_end_transaction(ss_aux, false); |
| ASSERT_EQ(0, rc); |
| |
| // Fill with zeroes (with commit) |
| for (uint32_t i = 0; i < 8; i++) { |
| rc = WriteZeroChunk(handle, i * blk, blk, true); |
| ASSERT_EQ((int)blk, rc); |
| } |
| |
| // check that test chunks is filled with zeroes |
| rc = ReadChunk(handle, blk, blk, blk, 0, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| rc = ReadChunk(handle, 2 * blk, blk, blk, 0, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // write test pattern (without commit) |
| rc = WritePattern(handle, blk, blk, blk, false); |
| ASSERT_EQ((int)blk, rc); |
| |
| // write test pattern (without commit) |
| rc = WritePattern(handle, 2 * blk, blk, blk, false); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read it back and check pattern |
| rc = ReadChunk(handle, blk, blk, 0, blk, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| rc = ReadChunk(handle, 2 * blk, blk, 0, blk, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // In aux session it still should be empty |
| rc = ReadChunk(handle_aux, blk, blk, blk, 0, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| rc = ReadChunk(handle_aux, 2 * blk, blk, blk, 0, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // commit current transaction |
| rc = storage_end_transaction(ss, true); |
| ASSERT_EQ(0, rc); |
| |
| // read same chunk back in primary session |
| rc = ReadChunk(handle, blk, blk, 0, blk, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| rc = ReadChunk(handle, 2 * blk, blk, 0, blk, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // read same chunk back in aux session |
| rc = ReadChunk(handle_aux, blk, blk, 0, blk, 0); |
| ASSERT_EQ(ERR_BUSY, rc); |
| |
| rc = ReadChunk(handle_aux, 2 * blk, blk, 0, blk, 0); |
| ASSERT_EQ(ERR_BUSY, rc); |
| |
| // abort transaction in aux session |
| rc = storage_end_transaction(ss_aux, false); |
| ASSERT_EQ(0, rc); |
| |
| // read same chunk again in aux session |
| rc = ReadChunk(handle_aux, blk, blk, 0, blk, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| rc = ReadChunk(handle_aux, 2 * blk, blk, 0, blk, 0); |
| ASSERT_EQ((int)blk, rc); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_close_file(handle_aux); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactCommitSetSize) { |
| int rc; |
| file_handle_t handle; |
| file_handle_t handle_aux; |
| size_t blk = 2048; |
| size_t exp_len = 32 * 1024; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname = "test_transact_commit_set_size"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // open the same file in separate session |
| rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // write data (with commit) |
| rc = WritePattern(handle, 0, exp_len, blk, true); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // same in aux session |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // set file size to half of original (no commit) |
| rc = storage_set_file_size(handle, (storage_off_t)exp_len / 2, 0); |
| ASSERT_EQ(0, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len / 2, fsize); |
| |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // set file size to 1/3 of original (no commit) |
| rc = storage_set_file_size(handle, (storage_off_t)exp_len / 3, 0); |
| ASSERT_EQ(0, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len / 3, fsize); |
| |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // commit current transaction |
| rc = storage_end_transaction(ss, true); |
| ASSERT_EQ(0, rc); |
| |
| // check file size (should be 1/3 of an original size) |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len / 3, fsize); |
| |
| // check file size from aux session |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(ERR_BUSY, rc); |
| |
| // abort transaction |
| rc = storage_end_transaction(ss_aux, false); |
| ASSERT_EQ(0, rc); |
| |
| // check again |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len / 3, fsize); |
| |
| // write data, increasing file size to exp_len (no commit) |
| rc = WritePattern(handle, 0, exp_len, blk, false); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // abort aux transaction |
| rc = storage_end_transaction(ss_aux, false); |
| ASSERT_EQ(0, rc); |
| |
| // check file size from aux session |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len / 3, fsize); |
| |
| // set file size without actually changing size, but ask to commit |
| rc = storage_set_file_size(handle, (storage_off_t)exp_len, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // abort aux transaction |
| rc = storage_end_transaction(ss_aux, false); |
| ASSERT_EQ(0, rc); |
| |
| // check file size from aux session |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_close_file(handle_aux); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactCommitDelete) { |
| int rc; |
| file_handle_t handle; |
| file_handle_t handle_aux; |
| size_t blk = 2048; |
| size_t exp_len = 32 * 1024; |
| const char* fname = "test_transact_commit_delete"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // write data (with commit) |
| rc = WritePattern(handle, 0, exp_len, blk, true); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| // close it |
| storage_close_file(handle); |
| |
| // open the same file in separate session |
| rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| storage_close_file(handle_aux); |
| |
| // delete file (without commit) |
| rc = storage_delete_file(ss, fname, 0); |
| ASSERT_EQ(0, rc); |
| |
| // try to open it (should fail) |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| // open the same file in separate session (should be fine) |
| rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| storage_close_file(handle_aux); |
| |
| // commit current transaction |
| rc = storage_end_transaction(ss, true); |
| ASSERT_EQ(0, rc); |
| |
| // try to open it (still fails) |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| // open the same file in separate session (should fail) |
| rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactCommitTruncate) { |
| int rc; |
| file_handle_t handle; |
| file_handle_t handle_aux; |
| size_t blk = 2048; |
| size_t exp_len = 32 * 1024; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname = "test_transact_commit_truncate"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // write data (with commit) |
| rc = WritePattern(handle, 0, exp_len, blk, true); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // close file |
| storage_close_file(handle); |
| |
| // check from different session |
| rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // open truncate file (without commit) |
| rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0); |
| ASSERT_EQ(0, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // commit current transaction |
| rc = storage_end_transaction(ss, true); |
| ASSERT_EQ(0, rc); |
| |
| // check file size (should be 0) |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| // check file size in aux session (should be ERR_BUSY) |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(ERR_BUSY, rc); |
| |
| // abort transaction in aux session |
| rc = storage_end_transaction(ss_aux, false); |
| ASSERT_EQ(0, rc); |
| |
| // check again |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_close_file(handle_aux); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactCommitCreate) { |
| int rc; |
| file_handle_t handle; |
| file_handle_t handle_aux; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname = "test_transact_commit_create"; |
| |
| // delete test file just in case |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| // check from different session |
| rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| // create file (without commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, 0); |
| ASSERT_EQ(0, rc); |
| |
| // check file size |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| // close file |
| storage_close_file(handle); |
| |
| // check from aux session (should fail) |
| rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| // commit current transaction |
| rc = storage_end_transaction(ss, true); |
| ASSERT_EQ(0, rc); |
| |
| // check open from normal session |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // check open from aux session (should succeed) |
| rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_close_file(handle_aux); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactCommitCreateMany) { |
| int rc; |
| file_handle_t handle1; |
| file_handle_t handle2; |
| file_handle_t handle1_aux; |
| file_handle_t handle2_aux; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname1 = "test_transact_commit_create1"; |
| const char* fname2 = "test_transact_commit_create2"; |
| |
| // delete test file just in case |
| storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); |
| storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); |
| |
| // create file (without commit) |
| rc = storage_open_file( |
| ss, &handle1, fname1, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, 0); |
| ASSERT_EQ(0, rc); |
| |
| // create file (without commit) |
| rc = storage_open_file( |
| ss, &handle2, fname2, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, 0); |
| ASSERT_EQ(0, rc); |
| |
| // check file sizes |
| rc = storage_get_file_size(handle1, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| rc = storage_get_file_size(handle1, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| // close files |
| storage_close_file(handle1); |
| storage_close_file(handle2); |
| |
| rc = storage_open_file(ss_aux, &handle1_aux, fname1, 0, 0); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| rc = storage_open_file(ss_aux, &handle2_aux, fname2, 0, 0); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| // commit current transaction |
| rc = storage_end_transaction(ss, true); |
| ASSERT_EQ(0, rc); |
| |
| // open from primary session |
| rc = storage_open_file(ss, &handle1, fname1, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| rc = storage_open_file(ss, &handle2, fname2, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // open from aux session |
| rc = storage_open_file(ss_aux, &handle1_aux, fname1, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| rc = storage_open_file(ss_aux, &handle2_aux, fname2, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // cleanup |
| storage_close_file(handle1); |
| storage_close_file(handle1_aux); |
| storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); |
| storage_close_file(handle2); |
| storage_close_file(handle2_aux); |
| storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactCommitWriteMany) { |
| int rc; |
| file_handle_t handle1; |
| file_handle_t handle2; |
| file_handle_t handle1_aux; |
| file_handle_t handle2_aux; |
| size_t blk = 2048; |
| size_t exp_len1 = 32 * 1024; |
| size_t exp_len2 = 31 * 1024; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname1 = "test_transact_commit_write_file1"; |
| const char* fname2 = "test_transact_commit_write_file2"; |
| |
| // open create truncate (with commit) |
| rc = storage_open_file( |
| ss, &handle1, fname1, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // open create truncate (with commit) |
| rc = storage_open_file( |
| ss, &handle2, fname2, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // open same files from aux session |
| rc = storage_open_file(ss_aux, &handle1_aux, fname1, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| rc = storage_open_file(ss_aux, &handle2_aux, fname2, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // file1: fill file with pattern (without commit) |
| rc = WritePattern(handle1, 0, exp_len1, blk, false); |
| ASSERT_EQ((int)exp_len1, rc); |
| |
| // file2: fill file with pattern (without commit) |
| rc = WritePattern(handle2, 0, exp_len2, blk, false); |
| ASSERT_EQ((int)exp_len2, rc); |
| |
| // check file size, it should be exp_len1 |
| rc = storage_get_file_size(handle1, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len1, fsize); |
| |
| // check file size, it should be exp_len2 |
| rc = storage_get_file_size(handle2, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len2, fsize); |
| |
| // check file sizes from aux session (should be 0) |
| rc = storage_get_file_size(handle1_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| rc = storage_get_file_size(handle2_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)0, fsize); |
| |
| // commit transaction |
| rc = storage_end_transaction(ss, true); |
| ASSERT_EQ(0, rc); |
| |
| // check file size, it should be exp_len1 |
| rc = storage_get_file_size(handle1, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len1, fsize); |
| |
| // check file size, it should be exp_len2 |
| rc = storage_get_file_size(handle2, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len2, fsize); |
| |
| // from aux session |
| rc = storage_get_file_size(handle1_aux, &fsize); |
| ASSERT_EQ(ERR_BUSY, rc); |
| |
| rc = storage_end_transaction(ss_aux, false); |
| ASSERT_EQ(0, rc); |
| |
| rc = storage_get_file_size(handle1_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len1, fsize); |
| |
| rc = storage_get_file_size(handle2_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len2, fsize); |
| |
| // check data |
| rc = ReadPatternEOF(handle1, 0, blk); |
| ASSERT_EQ((int)exp_len1, rc); |
| |
| rc = ReadPatternEOF(handle2, 0, blk); |
| ASSERT_EQ((int)exp_len2, rc); |
| |
| rc = ReadPatternEOF(handle1_aux, 0, blk); |
| ASSERT_EQ((int)exp_len1, rc); |
| |
| rc = ReadPatternEOF(handle2_aux, 0, blk); |
| ASSERT_EQ((int)exp_len2, rc); |
| |
| // cleanup |
| storage_close_file(handle1); |
| storage_close_file(handle1_aux); |
| storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); |
| storage_close_file(handle2); |
| storage_close_file(handle2_aux); |
| storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactCommitDeleteCreate) { |
| int rc; |
| file_handle_t handle; |
| file_handle_t handle_aux; |
| size_t blk = 2048; |
| size_t exp_len = 32 * 1024; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname = "test_transact_delete_create"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // write data (with commit) |
| rc = WritePattern(handle, 0, exp_len, blk, true); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| // close it |
| storage_close_file(handle); |
| |
| // delete file (without commit) |
| rc = storage_delete_file(ss, fname, 0); |
| ASSERT_EQ(0, rc); |
| |
| // try to open it (should fail) |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| // try to open it in aux session (should succeed) |
| rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // create file with the same name (no commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, 0); |
| ASSERT_EQ(0, rc); |
| |
| // write half of data (with commit) |
| rc = WritePattern(handle, 0, exp_len / 2, blk, true); |
| ASSERT_EQ((int)exp_len / 2, rc); |
| |
| // check file size (should be half) |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len / 2, fsize); |
| |
| // commit transaction |
| rc = storage_end_transaction(ss, true); |
| ASSERT_EQ(0, rc); |
| |
| // check data |
| rc = ReadPatternEOF(handle, 0, blk); |
| ASSERT_EQ((int)exp_len / 2, rc); |
| |
| // check from aux session |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| // abort trunsaction |
| rc = storage_end_transaction(ss_aux, false); |
| ASSERT_EQ(0, rc); |
| |
| // and try again |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| // close file and reopen it again |
| storage_close_file(handle_aux); |
| rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| rc = storage_get_file_size(handle_aux, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len / 2, fsize); |
| |
| rc = ReadPatternEOF(handle_aux, 0, blk); |
| ASSERT_EQ((int)exp_len / 2, rc); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_close_file(handle_aux); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactRewriteExistingTruncate) { |
| int rc; |
| file_handle_t handle; |
| size_t blk = 2048; |
| const char* fname = "test_transact_rewrite_existing_truncate"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // close it |
| storage_close_file(handle); |
| |
| // up |
| for (uint32_t i = 1; i < 32; i++) { |
| // open truncate (no commit) |
| rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, |
| 0); |
| ASSERT_EQ(0, rc); |
| |
| // write data (with commit) |
| rc = WritePattern(handle, 0, i * blk, blk, true); |
| ASSERT_EQ((int)(i * blk), rc); |
| |
| // close |
| storage_close_file(handle); |
| } |
| |
| // down |
| for (uint32_t i = 1; i < 32; i++) { |
| // open truncate (no commit) |
| rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, |
| 0); |
| ASSERT_EQ(0, rc); |
| |
| // write data (with commit) |
| rc = WritePattern(handle, 0, (32 - i) * blk, blk, true); |
| ASSERT_EQ((int)((32 - i) * blk), rc); |
| |
| // close |
| storage_close_file(handle); |
| } |
| |
| // cleanup |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactRewriteExistingSetSize) { |
| int rc; |
| file_handle_t handle; |
| size_t blk = 2048; |
| const char* fname = "test_transact_rewrite_existing_set_size"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // close it |
| storage_close_file(handle); |
| |
| // up |
| for (unsigned int i = 1; i < 32; i++) { |
| // open truncate (no commit) |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // write data (with commit) |
| rc = WritePattern(handle, 0, i * blk, blk, false); |
| ASSERT_EQ((int)(i * blk), rc); |
| |
| // update size (with commit) |
| rc = storage_set_file_size(handle, i * blk, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // close |
| storage_close_file(handle); |
| } |
| |
| // down |
| for (unsigned int i = 1; i < 32; i++) { |
| // open trancate (no commit) |
| rc = storage_open_file(ss, &handle, fname, 0, 0); |
| ASSERT_EQ(0, rc); |
| |
| // write data (with commit) |
| rc = WritePattern(handle, 0, (32 - i) * blk, blk, false); |
| ASSERT_EQ((int)((32 - i) * blk), rc); |
| |
| // update size (with commit) |
| rc = storage_set_file_size(handle, (32 - i) * blk, STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // close |
| storage_close_file(handle); |
| } |
| |
| // cleanup |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| TEST_F(StorageTest, TransactResumeAfterNonFatalError) { |
| int rc; |
| file_handle_t handle; |
| file_handle_t handle1; |
| size_t blk = 2048; |
| size_t exp_len = 32 * 1024; |
| storage_off_t fsize = (storage_off_t)(-1); |
| const char* fname = "test_transact_resume_writes"; |
| |
| // open create truncate file (with commit) |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(0, rc); |
| |
| // write (without commit) |
| rc = WritePattern(handle, 0, exp_len / 2, blk, false); |
| ASSERT_EQ((int)exp_len / 2, rc); |
| |
| // issue some commands that should fail with non-fatal errors |
| |
| // read past end of file |
| uint32_t val = 0xDEADBEEF; |
| rc = storage_read(handle, exp_len / 2 + 1, &val, sizeof(val)); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| // open non existing file |
| rc = storage_open_file(ss, &handle1, "foo", STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| // delete non-existing file |
| rc = storage_delete_file(ss, "foo", STORAGE_OP_COMPLETE); |
| ASSERT_EQ(ERR_NOT_FOUND, rc); |
| |
| // then resume writinga (without commit) |
| rc = WritePattern(handle, exp_len / 2, exp_len / 2, blk, false); |
| ASSERT_EQ((int)exp_len / 2, rc); |
| |
| // commit current transaction |
| rc = storage_end_transaction(ss, true); |
| ASSERT_EQ(0, rc); |
| |
| // check file size, it should be exp_len |
| rc = storage_get_file_size(handle, &fsize); |
| ASSERT_EQ(0, rc); |
| ASSERT_EQ((storage_off_t)exp_len, fsize); |
| |
| // check data |
| rc = ReadPatternEOF(handle, 0, blk); |
| ASSERT_EQ((int)exp_len, rc); |
| |
| // cleanup |
| storage_close_file(handle); |
| storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); |
| |
| test_abort:; |
| } |
| |
| /* |
| * Test that every API that should allow checkpointing does (when provisioning |
| * is enabled) |
| */ |
| TEST_F(StorageTest, ENABLED_ON_EMULATOR_ONLY(CheckpointApis)) { |
| int rc; |
| file_handle_t handle; |
| size_t blk = 2048; |
| size_t len = 32 * blk; |
| const char* fname = "test_checkpoint_create"; |
| const char* new_fname = "test_checkpoint_move"; |
| |
| if (!system_state_provisioning_allowed()) { |
| trusty_unittest_printf( |
| "[ SKIPPED ] CheckpointApis - Provisioning is not allowed\n"); |
| return; |
| } |
| |
| /* We don't allow OP_CHECKPOINT without STORAGE_OP_COMPLETE */ |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STORAGE_OP_CHECKPOINT); |
| ASSERT_EQ(ERR_NOT_VALID, rc); |
| |
| /* open create truncate file (with checkpoint) */ |
| rc = storage_open_file( |
| ss, &handle, fname, |
| STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, |
| STOR
|