trusty: storage: add tests

Change-Id: I40c9ea47e1304ef35db3d5936b001b63f3a9795e
diff --git a/trusty/storage/tests/Android.mk b/trusty/storage/tests/Android.mk
new file mode 100644
index 0000000..71c904d
--- /dev/null
+++ b/trusty/storage/tests/Android.mk
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := secure-storage-unit-test
+LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
+LOCAL_STATIC_LIBRARIES := \
+	libtrustystorageinterface \
+	libtrustystorage \
+	libtrusty \
+	liblog
+LOCAL_SRC_FILES := main.cpp
+include $(BUILD_NATIVE_TEST)
+
diff --git a/trusty/storage/tests/main.cpp b/trusty/storage/tests/main.cpp
new file mode 100644
index 0000000..a771b87
--- /dev/null
+++ b/trusty/storage/tests/main.cpp
@@ -0,0 +1,3040 @@
+/*
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <gtest/gtest.h>
+
+#include <trusty/lib/storage.h>
+
+#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+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));
+    while (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));
+    while (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);
+    while (cnt--) {
+        if (*buf != val)
+            return false;
+        buf++;
+    }
+    return true;
+}
+
+using testing::TestWithParam;
+
+class StorageServiceTest : public virtual TestWithParam<const char *> {
+public:
+    StorageServiceTest() {}
+    virtual ~StorageServiceTest() {}
+
+    virtual void SetUp() {
+        port_ = GetParam();
+        test_buf_ = NULL;
+        aux_session_ = STORAGE_INVALID_SESSION;
+        int rc = storage_open_session(TRUSTY_DEVICE_NAME, &session_, port_);
+        ASSERT_EQ(0, rc);
+    }
+
+    virtual void TearDown() {
+        if (test_buf_) {
+            delete[] test_buf_;
+            test_buf_ = NULL;
+        }
+        storage_close_session(session_);
+
+        if (aux_session_ != STORAGE_INVALID_SESSION) {
+            storage_close_session(aux_session_);
+            aux_session_ = STORAGE_INVALID_SESSION;
+        }
+    }
+
+    void WriteReadAtOffsetHelper(file_handle_t handle, size_t blk, size_t cnt, bool complete);
+
+    void WriteZeroChunk(file_handle_t handle, storage_off_t off, size_t chunk_len, bool complete );
+    void WritePatternChunk(file_handle_t handle, storage_off_t off, size_t chunk_len, bool complete);
+    void WritePattern(file_handle_t handle, storage_off_t off, size_t data_len, size_t chunk_len, bool complete);
+
+    void 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);
+    void ReadPattern(file_handle_t handle, storage_off_t off, size_t data_len, size_t chunk_len);
+    void ReadPatternEOF(file_handle_t handle, storage_off_t off, size_t chunk_len, size_t exp_len);
+
+protected:
+    const char *port_;
+    uint32_t *test_buf_;
+    storage_session_t session_;
+    storage_session_t aux_session_;
+};
+
+INSTANTIATE_TEST_CASE_P(SS_TD_Tests, StorageServiceTest,   ::testing::Values(STORAGE_CLIENT_TD_PORT));
+INSTANTIATE_TEST_CASE_P(SS_TDEA_Tests, StorageServiceTest, ::testing::Values(STORAGE_CLIENT_TDEA_PORT));
+INSTANTIATE_TEST_CASE_P(SS_TP_Tests, StorageServiceTest,   ::testing::Values(STORAGE_CLIENT_TP_PORT));
+
+
+void StorageServiceTest::WriteZeroChunk(file_handle_t handle, storage_off_t off,
+                                       size_t chunk_len, bool complete)
+{
+    int rc;
+    uint32_t data_buf[chunk_len/sizeof(uint32_t)];
+
+    ASSERT_PRED1(is_valid_size, chunk_len);
+    ASSERT_PRED1(is_valid_offset, off);
+
+    memset(data_buf, 0, chunk_len);
+
+    rc = storage_write(handle, off, data_buf, sizeof(data_buf),
+                       complete ? STORAGE_OP_COMPLETE : 0);
+    ASSERT_EQ((int)chunk_len, rc);
+}
+
+void StorageServiceTest::WritePatternChunk(file_handle_t handle, storage_off_t off,
+                                           size_t chunk_len, bool complete)
+{
+    int rc;
+    uint32_t data_buf[chunk_len/sizeof(uint32_t)];
+
+    ASSERT_PRED1(is_valid_size, chunk_len);
+    ASSERT_PRED1(is_valid_offset, off);
+
+    fill_pattern32(data_buf, chunk_len, off);
+
+    rc = storage_write(handle, off, data_buf, sizeof(data_buf),
+                       complete ? STORAGE_OP_COMPLETE : 0);
+    ASSERT_EQ((int)chunk_len, rc);
+}
+
+void StorageServiceTest::WritePattern(file_handle_t handle, storage_off_t off,
+                                      size_t data_len, size_t chunk_len, bool complete)
+{
+    ASSERT_PRED1(is_valid_size, data_len);
+    ASSERT_PRED1(is_valid_size, chunk_len);
+
+    while (data_len) {
+        if (data_len < chunk_len)
+            chunk_len = data_len;
+        WritePatternChunk(handle, off, chunk_len, (chunk_len == data_len) && complete);
+        ASSERT_FALSE(HasFatalFailure());
+        off += chunk_len;
+        data_len -= chunk_len;
+    }
+}
+
+void StorageServiceTest::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_PRED1(is_valid_size, chunk_len);
+    ASSERT_PRED1(is_valid_offset, off);
+    ASSERT_EQ(head_len + pattern_len + tail_len, chunk_len);
+
+    rc = storage_read(handle, off, data_buf, chunk_len);
+    ASSERT_EQ((int)chunk_len, rc);
+
+    if (head_len) {
+        ASSERT_TRUE(check_value32((const uint32_t *)data_ptr, head_len, 0));
+        data_ptr += head_len;
+        off += head_len;
+    }
+
+    if (pattern_len) {
+        ASSERT_TRUE(check_pattern32((const uint32_t *)data_ptr, pattern_len, off));
+        data_ptr += pattern_len;
+    }
+
+    if (tail_len) {
+        ASSERT_TRUE(check_value32((const uint32_t *)data_ptr, tail_len, 0));
+    }
+}
+
+void StorageServiceTest::ReadPattern(file_handle_t handle, storage_off_t off,
+                                     size_t data_len, size_t chunk_len)
+{
+    int rc;
+    uint32_t data_buf[chunk_len/sizeof(uint32_t)];
+
+    ASSERT_PRED1(is_valid_size, chunk_len);
+    ASSERT_PRED1(is_valid_size, data_len);
+    ASSERT_PRED1(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));
+        ASSERT_EQ((int)chunk_len, rc);
+        ASSERT_TRUE(check_pattern32(data_buf, chunk_len, off));
+        off += chunk_len;
+        data_len -= chunk_len;
+    }
+}
+
+void StorageServiceTest::ReadPatternEOF(file_handle_t handle, storage_off_t off,
+                                        size_t chunk_len, size_t exp_len)
+{
+    int rc;
+    size_t bytes_read = 0;
+    uint32_t data_buf[chunk_len/sizeof(uint32_t)];
+
+    ASSERT_PRED1(is_valid_size, chunk_len);
+    ASSERT_PRED1(is_32bit_aligned, exp_len);
+
+    while (true) {
+         rc = storage_read(handle, off, data_buf, sizeof(data_buf));
+         ASSERT_GE(rc, 0);
+         if (rc == 0)
+             break; // end of file reached
+         ASSERT_PRED1(is_valid_size, (size_t)rc);
+         ASSERT_TRUE(check_pattern32(data_buf, rc, off));
+         off += rc;
+         bytes_read += rc;
+    }
+    ASSERT_EQ(bytes_read, exp_len);
+}
+
+TEST_P(StorageServiceTest, CreateDelete) {
+    int rc;
+    file_handle_t handle;
+    const char *fname = "test_create_delete_file";
+
+    // make sure test file does not exist (expect success or -ENOENT)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    rc = (rc == -ENOENT) ? 0 : rc;
+    ASSERT_EQ(0, rc);
+
+    // one more time (expect -ENOENT only)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-ENOENT, rc);
+
+    // create file (expect 0)
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // try to create it again while it is still opened (expect -EEXIST)
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-EEXIST, rc);
+
+    // close it
+    storage_close_file(handle);
+
+    // try to create it again while it is closed (expect -EEXIST)
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-EEXIST, rc);
+
+    // delete file (expect 0)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // one more time (expect -ENOENT)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-ENOENT, rc);
+}
+
+
+TEST_P(StorageServiceTest, DeleteOpened) {
+    int rc;
+    file_handle_t handle;
+    const char *fname = "delete_opened_test_file";
+
+    // make sure test file does not exist (expect success or -ENOENT)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    rc = (rc == -ENOENT) ? 0 : rc;
+    ASSERT_EQ(0, rc);
+
+    // one more time (expect -ENOENT)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-ENOENT, rc);
+
+    // open/create file (expect 0)
+    rc = storage_open_file(session_, &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(session_, fname, STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // one more time (expect -ENOENT)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-ENOENT, rc);
+
+    // close file
+    storage_close_file(handle);
+
+    // one more time (expect -ENOENT)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-ENOENT, rc);
+}
+
+
+TEST_P(StorageServiceTest, 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 -ENOENT)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    rc = (rc == -ENOENT) ? 0 : rc;
+    ASSERT_EQ(0, rc);
+
+    // open non-existing file (expect -ENOENT)
+    rc = storage_open_file(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(-ENOENT, rc);
+
+    // create file (expect 0)
+    rc = storage_open_file(session_, &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(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    // close it
+    storage_close_file(handle);
+
+    // delete file (expect 0)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+}
+
+
+TEST_P(StorageServiceTest, OpenOrCreate) {
+    int rc;
+    file_handle_t handle;
+    const char *fname = "test_open_create_file";
+
+    // make sure test file does not exist (expect success or -ENOENT)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    rc = (rc == -ENOENT) ? 0 : rc;
+    ASSERT_EQ(0, rc);
+
+    // open/create a non-existing file (expect 0)
+    rc = storage_open_file(session_, &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(session_, &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(session_, fname, STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+}
+
+
+TEST_P(StorageServiceTest, OpenCreateDeleteCharset) {
+    int rc;
+    file_handle_t handle;
+    const char *fname = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz_01234.56789";
+
+    // open/create file (expect 0)
+    rc = storage_open_file(session_, &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(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+    storage_close_file(handle);
+
+    // delete file (expect 0)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // open again
+    rc = storage_open_file(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(-ENOENT, rc);
+}
+
+
+TEST_P(StorageServiceTest, 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 -ENOENT)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    rc = (rc == -ENOENT) ? 0 : rc;
+    ASSERT_EQ(0, rc);
+
+    // create file.
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write a bunch of blocks (sequentially)
+    WritePattern(handle, 0, 32 * blk, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ReadPattern(handle, 0, 32 * blk, blk);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close file
+    storage_close_file(handle);
+
+    // open the same file again
+    rc = storage_open_file(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    // read data back (sequentially) and check pattern again
+    ReadPattern(handle, 0, 32 * blk, blk);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, 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 -ENOENT)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    rc = (rc == -ENOENT) ? 0 : rc;
+    ASSERT_EQ(0, rc);
+
+    // create file.
+    rc = storage_open_file(session_, &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
+    WritePatternChunk(handle, 0, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ReadPattern(handle, 0, blk, blk);
+    ASSERT_FALSE(HasFatalFailure());
+
+     // close file
+    storage_close_file(handle);
+
+    // reopen with truncate
+    rc = storage_open_file(session_, &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(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, 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(session_, &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(session_, &handle1, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    // open the same file second time (expect error)
+    rc = storage_open_file(session_, &handle2, fname, 0, 0);
+    ASSERT_NE(0, rc);
+
+    storage_close_file(handle1);
+
+    // delete file (expect 0)
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // open deleted file (expect -ENOENT)
+    rc = storage_open_file(session_, &handle3, fname, 0, 0);
+    ASSERT_EQ(-ENOENT, rc);
+}
+
+
+TEST_P(StorageServiceTest, 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 (uint i = 0; i < ARRAY_SIZE(handles); ++i) {
+        snprintf(filename, sizeof(filename), fname_fmt, i);
+        rc = storage_open_file(session_, &handles[i], filename,
+                               STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE);
+        ASSERT_EQ(0, rc);
+    }
+
+    // check that all handles are different
+    for (uint i = 0; i < ARRAY_SIZE(handles)-1; i++) {
+        for (uint j = i+1; j < ARRAY_SIZE(handles); j++) {
+            ASSERT_NE(handles[i], handles[j]);
+        }
+    }
+
+    // close them all
+    for (uint i = 0; i < ARRAY_SIZE(handles); ++i) {
+        storage_close_file(handles[i]);
+    }
+
+    // open all files without CREATE flags (expect 0)
+    for (uint i = 0; i < ARRAY_SIZE(handles); ++i) {
+        snprintf(filename, sizeof(filename), fname_fmt, i);
+        rc = storage_open_file(session_, &handles[i], filename, 0, 0);
+        ASSERT_EQ(0, rc);
+    }
+
+    // check that all handles are different
+    for (uint i = 0; i < ARRAY_SIZE(handles)-1; i++) {
+        for (uint j = i+1; j < ARRAY_SIZE(handles); j++) {
+            ASSERT_NE(handles[i], handles[j]);
+        }
+    }
+
+    // close and remove all test files
+    for (uint i = 0; i < ARRAY_SIZE(handles); ++i) {
+        storage_close_file(handles[i]);
+        snprintf(filename, sizeof(filename), fname_fmt, i);
+        rc = storage_delete_file(session_, filename, STORAGE_OP_COMPLETE);
+        ASSERT_EQ(0, rc);
+    }
+}
+
+
+TEST_P(StorageServiceTest, 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(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write block at offset 0
+    WritePatternChunk(handle, 0, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close file
+    storage_close_file(handle);
+
+    // open same file again
+    rc = storage_open_file(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    // read the whole block back and check pattern again
+    ReadPattern(handle, 0, blk, blk);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // 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)
+    ReadPatternEOF(handle, blk/2, blk, blk/2);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read past end of file
+    rc = storage_read(handle, blk + 2, &val, sizeof(val));
+    ASSERT_EQ(-EINVAL, rc);
+
+    // cleanup
+    storage_close_file(handle);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, 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(session_, &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
+    WritePatternChunk(handle, 0, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // check size
+    rc = storage_get_file_size(handle, &size);
+    ASSERT_EQ(0, rc);
+    ASSERT_EQ(blk, size);
+
+    // write another block
+    WritePatternChunk(handle, blk, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // 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(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, 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(session_, &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
+    WritePatternChunk(handle, 0, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // 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(session_, &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
+    ReadPatternEOF(handle, 0, blk, blk/2);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // 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
+    ReadPatternEOF(handle, 0, blk, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+void StorageServiceTest::WriteReadAtOffsetHelper(file_handle_t handle, size_t blk, size_t cnt, bool complete)
+{
+    storage_off_t off1 = blk;
+    storage_off_t off2 = blk * (cnt-1);
+
+    // write known pattern data at non-zero offset1
+    WritePatternChunk(handle, off1, blk, complete);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // write known pattern data at non-zero offset2
+    WritePatternChunk(handle, off2, blk, complete);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read data back at offset1
+    ReadPattern(handle, off1, blk, blk);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read data back at offset2
+    ReadPattern(handle, off2, blk, blk);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read partially written data at end of file(expect to get data only, no padding)
+    ReadPatternEOF(handle, off2 + blk/2, blk, blk/2);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read data at offset 0 (expect success and zero data)
+    ReadChunk(handle, 0, blk, blk, 0, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read data from gap (expect success and zero data)
+    ReadChunk(handle, off1 + blk, blk, blk, 0, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read partially written data (start pointing within written data)
+    // (expect to get written data back and zeroes at the end)
+    ReadChunk(handle, off1 + blk/2, blk, 0, blk/2, blk/2);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read partially written data (start pointing withing unwritten data)
+    // expect to get zeroes at the beginning and proper data at the end
+    ReadChunk(handle, off1 - blk/2, blk, blk/2, blk/2, 0);
+    ASSERT_FALSE(HasFatalFailure());
+}
+
+
+TEST_P(StorageServiceTest, 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(session_, &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 (uint i = 0; i < blk_cnt; i++) {
+        WriteZeroChunk(handle, i * blk, blk, true);
+        ASSERT_FALSE(HasFatalFailure());
+    }
+
+    WriteReadAtOffsetHelper(handle, blk, blk_cnt, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, WriteSparse) {
+    int rc;
+    file_handle_t handle;
+    const char *fname = "test_write_sparse";
+
+    // open/create/truncate file.
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write value past en of file
+    uint32_t val = 0xDEADBEEF;
+    rc = storage_write(handle, 1, &val, sizeof(val), STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-EINVAL, rc);
+
+    // cleanup
+    storage_close_file(handle);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+// Persistent 32k
+
+TEST_P(StorageServiceTest, 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.
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write a bunch of blocks filled with pattern
+    WritePattern(handle, 0, file_size, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close but do not delete file
+    storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, 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(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    ReadPatternEOF(handle, 0, 2048, exp_len);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ReadPatternEOF(handle, 0, 1024, exp_len);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ReadPatternEOF(handle, 0,  332, exp_len);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close but do not delete file
+    storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, CleanUpPersistent32K) {
+    int rc;
+    const char *fname = "test_persistent_32K_file";
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    rc = (rc == -ENOENT) ? 0 : rc;
+    ASSERT_EQ(0, rc);
+}
+
+// Persistent 1M
+TEST_P(StorageServiceTest, CreatePersistent1M_4040) {
+    int rc;
+    file_handle_t handle;
+    size_t file_size = 1024 * 1024;
+    const char *fname = "test_persistent_1M_file";
+
+    // create/truncate file.
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write a bunch of blocks filled with pattern
+    WritePattern(handle, 0, file_size, 4040, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close but do not delete file
+    storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, CreatePersistent1M_2032) {
+    int rc;
+    file_handle_t handle;
+    size_t file_size = 1024 * 1024;
+    const char *fname = "test_persistent_1M_file";
+
+    // create/truncate file.
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write a bunch of blocks filled with pattern
+    WritePattern(handle, 0, file_size, 2032, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close but do not delete file
+    storage_close_file(handle);
+}
+
+
+TEST_P(StorageServiceTest, CreatePersistent1M_496) {
+    int rc;
+    file_handle_t handle;
+    size_t file_size = 1024 * 1024;
+    const char *fname = "test_persistent_1M_file";
+
+    // create/truncate file.
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write a bunch of blocks filled with pattern
+    WritePattern(handle, 0, file_size, 496, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close but do not delete file
+    storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, CreatePersistent1M_240) {
+    int rc;
+    file_handle_t handle;
+    size_t file_size = 1024 * 1024;
+    const char *fname = "test_persistent_1M_file";
+
+    // create/truncate file.
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write a bunch of blocks filled with pattern
+    WritePattern(handle, 0, file_size, 240, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close but do not delete file
+    storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, ReadPersistent1M_4040) {
+    int rc;
+    file_handle_t handle;
+    size_t exp_len = 1024 * 1024;
+    const char *fname = "test_persistent_1M_file";
+
+    // create/truncate file.
+    rc = storage_open_file(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    ReadPatternEOF(handle, 0, 4040, exp_len);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close but do not delete file
+    storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, ReadPersistent1M_2032) {
+    int rc;
+    file_handle_t handle;
+    size_t exp_len = 1024 * 1024;
+    const char *fname = "test_persistent_1M_file";
+
+    // create/truncate file.
+    rc = storage_open_file(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    ReadPatternEOF(handle, 0, 2032, exp_len);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close but do not delete file
+    storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, ReadPersistent1M_496) {
+    int rc;
+    file_handle_t handle;
+    size_t exp_len = 1024 * 1024;
+    const char *fname = "test_persistent_1M_file";
+
+    // create/truncate file.
+    rc = storage_open_file(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    ReadPatternEOF(handle, 0, 496, exp_len);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close but do not delete file
+    storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, ReadPersistent1M_240) {
+    int rc;
+    file_handle_t handle;
+    size_t exp_len = 1024 * 1024;
+    const char *fname = "test_persistent_1M_file";
+
+    // create/truncate file.
+    rc = storage_open_file(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    ReadPatternEOF(handle, 0, 240, exp_len);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close but do not delete file
+    storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, CleanUpPersistent1M) {
+    int rc;
+    const char *fname = "test_persistent_1M_file";
+    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+    rc = (rc == -ENOENT) ? 0 : rc;
+    ASSERT_EQ(0, rc);
+}
+
+TEST_P(StorageServiceTest, WriteReadLong) {
+    int rc;
+    file_handle_t handle;
+    size_t wc = 10000;
+    const char *fname = "test_write_read_long";
+
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    test_buf_ = new uint32_t[wc];
+    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);
+    ASSERT_TRUE(check_pattern32(test_buf_, wc * sizeof(uint32_t), 0));
+
+    // cleanup
+    storage_close_file(handle);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+// Negative tests
+
+TEST_P(StorageServiceTest, 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(session_, &handle, fname1,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-EINVAL, rc);
+
+    rc = storage_open_file(session_, &handle, fname2,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-EINVAL, rc);
+
+    rc = storage_open_file(session_, &handle, fname3,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-EINVAL, rc);
+
+    /* max name */
+    memset(max_name, 'a', sizeof(max_name));
+    max_name[sizeof(max_name)-1] = 0;
+
+    rc = storage_open_file(session_, &handle, max_name,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-EINVAL, rc);
+
+    max_name[sizeof(max_name)-2] = 0;
+    rc = storage_open_file(session_, &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(session_, max_name, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, BadFileHnadle) {
+    int rc;
+    file_handle_t handle;
+    file_handle_t handle1;
+    const char *fname = "test_invalid_file_handle";
+
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    handle1 = handle + 1;
+
+    // write to invalid file handle
+    uint32_t val = 0xDEDBEEF;
+    rc = storage_write(handle1,  0, &val, sizeof(val), STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-EINVAL, rc);
+
+    // read from invalid handle
+    rc = storage_read(handle1,  0, &val, sizeof(val));
+    ASSERT_EQ(-EINVAL, rc);
+
+    // set size
+    rc = storage_set_file_size(handle1,  0, STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-EINVAL, rc);
+
+    // get size
+    storage_off_t fsize = (storage_off_t)(-1);
+    rc = storage_get_file_size(handle1,  &fsize);
+    ASSERT_EQ(-EINVAL, rc);
+
+    // close (there is no way to check errors here)
+    storage_close_file(handle1);
+
+    storage_close_file(handle);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, ClosedFileHnadle) {
+    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(session_, &handle1, fname1,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    rc = storage_open_file(session_, &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(-EINVAL, rc);
+
+    // read from invalid handle
+    rc = storage_read(handle1,  0, &val, sizeof(val));
+    ASSERT_EQ(-EINVAL, rc);
+
+    // set size
+    rc = storage_set_file_size(handle1,  0, STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-EINVAL, rc);
+
+    // get size
+    storage_off_t fsize = (storage_off_t)(-1);
+    rc = storage_get_file_size(handle1,  &fsize);
+    ASSERT_EQ(-EINVAL, rc);
+
+    // close (there is no way to check errors here)
+    storage_close_file(handle1);
+
+    // clean up
+    storage_close_file(handle2);
+    storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);
+    storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);
+}
+
+// Transactions
+
+TEST_P(StorageServiceTest, TransactDiscardInactive) {
+    int rc;
+
+    // discard current transaction (there should not be any)
+    rc = storage_end_transaction(session_, false);
+    ASSERT_EQ(0, rc);
+
+    // try it again
+    rc = storage_end_transaction(session_, false);
+    ASSERT_EQ(0, rc);
+}
+
+TEST_P(StorageServiceTest, TransactCommitInactive) {
+    int rc;
+
+    // try to commit current transaction
+    rc = storage_end_transaction(session_, true);
+    ASSERT_EQ(0, rc);
+
+    // try it again
+    rc = storage_end_transaction(session_, true);
+    ASSERT_EQ(0, rc);
+}
+
+TEST_P(StorageServiceTest, 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(session_, &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)
+    WritePattern(handle, 0, exp_len, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // 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(session_, 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(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, 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(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write data with commit
+    WritePattern(handle, 0, exp_len/2, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // write data without commit
+    WritePattern(handle, exp_len/2, exp_len/2, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // 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(session_, 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
+    ReadPatternEOF(handle, 0, blk, exp_len/2);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, 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(session_, &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 (uint i = 0; i < 32; i++) {
+        WriteZeroChunk(handle, i * blk, blk, true);
+        ASSERT_FALSE(HasFatalFailure());
+    }
+
+    // check that test chunk is filled with zeroes
+    ReadChunk(handle, blk, blk, blk, 0, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // write test pattern (without commit)
+    WritePattern(handle, blk, blk, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read it back an check pattern
+    ReadChunk(handle, blk, blk, 0, blk, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // abort current transaction
+    rc = storage_end_transaction(session_, false);
+    ASSERT_EQ(0, rc);
+
+    // read same chunk back (should be filled with zeros)
+    ReadChunk(handle, blk, blk, blk, 0, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, 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(session_, &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(session_, &handle2, fname2,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // file1: fill file with pattern (without commit)
+    WritePattern(handle1, 0, exp_len1, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // file2: fill file with pattern (without commit)
+    WritePattern(handle2, 0, exp_len2, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // 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);
+
+    // commit transaction
+    rc = storage_end_transaction(session_, false);
+    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)0, 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)0, fsize);
+
+    // check data
+    ReadPatternEOF(handle1, 0, blk, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ReadPatternEOF(handle2, 0, blk, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle1);
+    storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);
+    storage_close_file(handle2);
+    storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, 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(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write data (with commit)
+    WritePattern(handle, 0, exp_len, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // 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(session_, &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(session_, 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(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, 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(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write data (with commit)
+    WritePattern(handle, 0, exp_len, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // 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(session_, 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(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, 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(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write data (with commit)
+    WritePattern(handle, 0, exp_len, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close it
+    storage_close_file(handle);
+
+    // delete file (without commit)
+    rc = storage_delete_file(session_, fname, 0);
+    ASSERT_EQ(0, rc);
+
+    // try to open it (should fail)
+    rc = storage_open_file(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(-ENOENT, rc);
+
+    // abort current transaction
+    rc = storage_end_transaction(session_, false);
+    ASSERT_EQ(0, rc);
+
+    // try to open it
+    rc = storage_open_file(session_, &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(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, TransactDiscardDelete2) {
+    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(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write data (with commit)
+    WritePattern(handle, 0, exp_len, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // delete file (without commit)
+    rc = storage_delete_file(session_, fname, 0);
+    ASSERT_EQ(0, rc);
+    storage_close_file(handle);
+
+    // try to open it (should fail)
+    rc = storage_open_file(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(-ENOENT, rc);
+
+    // abort current transaction
+    rc = storage_end_transaction(session_, false);
+    ASSERT_EQ(0, rc);
+
+    // try to open it
+    rc = storage_open_file(session_, &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(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, TransactDiscardCreate) {
+    int rc;
+    file_handle_t handle;
+    const char *fname = "test_transact_discard_create_excl";
+
+    // delete test file just in case
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+
+    // create file (without commit)
+    rc = storage_open_file(session_, &handle, fname,
+                               STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+                               0);
+    ASSERT_EQ(0, rc);
+
+    // abort current transaction
+    rc = storage_end_transaction(session_, false);
+    ASSERT_EQ(0, rc);
+
+    // cleanup
+    storage_close_file(handle);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, 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 second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    // open create truncate file (with commit)
+    rc = storage_open_file(session_, &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(aux_session_, &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)
+    WritePattern(handle, 0, exp_len/2, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // write more data in primary session (without commit)
+    WritePattern(handle, exp_len/2, exp_len/2, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // 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(session_, true);
+    ASSERT_EQ(0, rc);
+
+    // check file size of aux session, should fail
+    rc = storage_get_file_size(handle_aux, &fsize);
+    ASSERT_EQ(-EBUSY, rc);
+
+    // abort transaction in aux session to recover
+    rc = storage_end_transaction(aux_session_, 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
+    ReadPatternEOF(handle, 0, blk, exp_len);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // check data in aux session
+    ReadPatternEOF(handle_aux, 0, blk, exp_len);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle);
+    storage_close_file(handle_aux);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, 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 second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    // open create truncate file (with commit)
+    rc = storage_open_file(session_, &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(aux_session_, &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(aux_session_,  false);
+    ASSERT_EQ(0, rc);
+
+    // Fill with zeroes (with commit)
+    for (uint i = 0; i < 8; i++) {
+        WriteZeroChunk(handle, i * blk, blk, true);
+        ASSERT_FALSE(HasFatalFailure());
+    }
+
+    // check that test chunks are filled with zeroes
+    ReadChunk(handle, blk, blk, blk, 0, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ReadChunk(handle, 2 * blk, blk, blk, 0, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // write test pattern (without commit)
+    WritePattern(handle, blk, blk, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // write test pattern (without commit)
+    WritePattern(handle, 2 * blk, blk, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read it back and check pattern
+    ReadChunk(handle, blk, blk, 0, blk, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ReadChunk(handle, 2 * blk, blk, 0, blk, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // In aux session it still should be empty
+    ReadChunk(handle_aux, blk, blk, blk, 0, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ReadChunk(handle_aux, 2 * blk, blk, blk, 0, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // commit current transaction
+    rc = storage_end_transaction(session_, true);
+    ASSERT_EQ(0, rc);
+
+    // read same chunks back in primary session
+    ReadChunk(handle, blk, blk, 0, blk, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ReadChunk(handle, 2 * blk, blk, 0, blk, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read same chunks back in aux session (should fail)
+    uint32_t val;
+    rc = storage_read(handle_aux, blk, &val, sizeof(val));
+    ASSERT_EQ(-EBUSY, rc);
+
+    rc = storage_read(handle_aux, 2 * blk, &val, sizeof(val));
+    ASSERT_EQ(-EBUSY, rc);
+
+    // abort transaction in aux session
+    rc = storage_end_transaction(aux_session_,  false);
+    ASSERT_EQ(0, rc);
+
+    // read same chunk again in aux session
+    ReadChunk(handle_aux, blk, blk, 0, blk, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ReadChunk(handle_aux, 2 * blk, blk, 0, blk, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+
+    // cleanup
+    storage_close_file(handle);
+    storage_close_file(handle_aux);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, 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 second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    // open create truncate file (with commit)
+    rc = storage_open_file(session_, &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(aux_session_, &handle_aux, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    // write data (with commit)
+    WritePattern(handle, 0, exp_len, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // 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(session_, 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(-EBUSY, rc);
+
+    // abort transaction in aux_session
+    rc = storage_end_transaction(aux_session_, 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);
+
+    // cleanup
+    storage_close_file(handle);
+    storage_close_file(handle_aux);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, 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 second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    // open create truncate file (with commit)
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write data (with commit)
+    WritePattern(handle, 0, exp_len, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close it
+    storage_close_file(handle);
+
+    // open the same file in separate session
+    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+    storage_close_file(handle_aux);
+
+    // delete file (without commit)
+    rc = storage_delete_file(session_, fname, 0);
+    ASSERT_EQ(0, rc);
+
+    // try to open it (should fail)
+    rc = storage_open_file(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(-ENOENT, rc);
+
+    // open the same file in separate session (should be fine)
+    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+    storage_close_file(handle_aux);
+
+    // commit current transaction
+    rc = storage_end_transaction(session_, true);
+    ASSERT_EQ(0, rc);
+
+    // try to open it in primary session (still fails)
+    rc = storage_open_file(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(-ENOENT, rc);
+
+    // open the same file in aux session (should also fail)
+    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+    ASSERT_EQ(-ENOENT, rc);
+}
+
+
+TEST_P(StorageServiceTest, 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 second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    // open create truncate file (with commit)
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write data (with commit)
+    WritePattern(handle, 0, exp_len, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // 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(aux_session_, &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(session_, &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(session_, 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 -EBUSY)
+    rc = storage_get_file_size(handle_aux, &fsize);
+    ASSERT_EQ(-EBUSY, rc);
+
+    // abort transaction in aux session
+    rc = storage_end_transaction(aux_session_, 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(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, 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";
+
+    // open second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    // delete test file just in case
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+
+    // check from aux session
+    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+    ASSERT_EQ(-ENOENT, rc);
+
+    // create file (without commit)
+    rc = storage_open_file(session_, &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(aux_session_, &handle_aux, fname, 0, 0);
+    ASSERT_EQ(-ENOENT, rc);
+
+    // commit current transaction
+    rc = storage_end_transaction(session_, true);
+    ASSERT_EQ(0, rc);
+
+    // check open from normal session
+    rc = storage_open_file(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    // check open from aux session (should succeed)
+    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    // cleanup
+    storage_close_file(handle);
+    storage_close_file(handle_aux);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, 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";
+
+    // open second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    // delete test file just in case
+    storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);
+    storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);
+
+    // create file (without commit)
+    rc = storage_open_file(session_, &handle1, fname1,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+                           0);
+    ASSERT_EQ(0, rc);
+
+    // create file (without commit)
+    rc = storage_open_file(session_, &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);
+
+    // open files from aux session
+    rc = storage_open_file(aux_session_, &handle1_aux, fname1, 0, 0);
+    ASSERT_EQ(-ENOENT, rc);
+
+    rc = storage_open_file(aux_session_, &handle2_aux, fname2, 0, 0);
+    ASSERT_EQ(-ENOENT, rc);
+
+    // commit current transaction
+    rc = storage_end_transaction(session_, true);
+    ASSERT_EQ(0, rc);
+
+    // open from primary session
+    rc = storage_open_file(session_, &handle1, fname1, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    rc = storage_open_file(session_, &handle2, fname2, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    // open from aux session
+    rc = storage_open_file(aux_session_, &handle1_aux, fname1, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    rc = storage_open_file(aux_session_, &handle2_aux, fname2, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    // cleanup
+    storage_close_file(handle1);
+    storage_close_file(handle1_aux);
+    storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);
+    storage_close_file(handle2);
+    storage_close_file(handle2_aux);
+    storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, 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 second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    // open create truncate (with commit)
+    rc = storage_open_file(session_, &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(session_, &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(aux_session_, &handle1_aux, fname1, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    rc = storage_open_file(aux_session_, &handle2_aux, fname2, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    // file1: fill file with pattern (without commit)
+    WritePattern(handle1, 0, exp_len1, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // file2: fill file with pattern (without commit)
+    WritePattern(handle2, 0, exp_len2, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // 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(session_, 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);
+
+    // check from aux session (should be -EBUSY)
+    rc = storage_get_file_size(handle1_aux, &fsize);
+    ASSERT_EQ(-EBUSY, rc);
+
+    // abort transaction in aux session
+    rc = storage_end_transaction(aux_session_, false);
+    ASSERT_EQ(0, rc);
+
+    // and check again
+    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
+    ReadPatternEOF(handle1, 0, blk, exp_len1);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ReadPatternEOF(handle2, 0, blk, exp_len2);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ReadPatternEOF(handle1_aux, 0, blk, exp_len1);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ReadPatternEOF(handle2_aux, 0, blk, exp_len2);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle1);
+    storage_close_file(handle1_aux);
+    storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);
+    storage_close_file(handle2);
+    storage_close_file(handle2_aux);
+    storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, 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 second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    // open create truncate file (with commit)
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write data (with commit)
+    WritePattern(handle, 0, exp_len, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close it
+    storage_close_file(handle);
+
+    // delete file (without commit)
+    rc = storage_delete_file(session_, fname, 0);
+    ASSERT_EQ(0, rc);
+
+    // try to open it (should fail)
+    rc = storage_open_file(session_, &handle, fname, 0, 0);
+    ASSERT_EQ(-ENOENT, rc);
+
+    // try to open it in aux session (should succeed)
+    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    // create file with the same name (no commit)
+    rc = storage_open_file(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+                           0);
+    ASSERT_EQ(0, rc);
+
+    // write half of data (with commit)
+    WritePattern(handle, 0, exp_len/2, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // 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(session_, true);
+    ASSERT_EQ(0, rc);
+
+    // check data from primary session
+    ReadPatternEOF(handle, 0, blk, exp_len/2);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // check from aux session (should fail)
+    rc = storage_get_file_size(handle_aux, &fsize);
+    ASSERT_EQ(-EINVAL, rc);
+
+    // abort trunsaction in aux session
+    rc = storage_end_transaction(aux_session_, false);
+    ASSERT_EQ(0, rc);
+
+    // and try again (should still fail)
+    rc = storage_get_file_size(handle_aux, &fsize);
+    ASSERT_EQ(-EINVAL, rc);
+
+    // close file and reopen it again
+    storage_close_file(handle_aux);
+    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+    ASSERT_EQ(0, rc);
+
+    // try it again (should succeed)
+    rc = storage_get_file_size(handle_aux, &fsize);
+    ASSERT_EQ(0, rc);
+    ASSERT_EQ((storage_off_t)exp_len/2, fsize);
+
+    // check data
+    ReadPatternEOF(handle_aux, 0, blk, exp_len/2);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle);
+    storage_close_file(handle_aux);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, 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(session_, &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 (uint i = 1; i < 32; i++) {
+        // open truncate (no commit)
+        rc = storage_open_file(session_, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0);
+        ASSERT_EQ(0, rc);
+
+        // write data (with commit)
+        WritePattern(handle, 0, i * blk, blk, true);
+        ASSERT_FALSE(HasFatalFailure());
+
+        // close
+        storage_close_file(handle);
+    }
+
+    // down
+    for (uint i = 1; i < 32; i++) {
+        // open truncate (no commit)
+        rc = storage_open_file(session_, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0);
+        ASSERT_EQ(0, rc);
+
+        // write data (with commit)
+        WritePattern(handle, 0, (32 - i) * blk, blk, true);
+        ASSERT_FALSE(HasFatalFailure());
+
+        // close
+        storage_close_file(handle);
+    }
+
+    // cleanup
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, 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(session_, &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 (uint i = 1; i < 32; i++) {
+        // open truncate (no commit)
+        rc = storage_open_file(session_, &handle, fname, 0, 0);
+        ASSERT_EQ(0, rc);
+
+        // write data (with commit)
+        WritePattern(handle, 0, i * blk, blk, false);
+        ASSERT_FALSE(HasFatalFailure());
+
+        // 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 (uint i = 1; i < 32; i++) {
+        // open trancate (no commit)
+        rc = storage_open_file(session_, &handle, fname, 0, 0);
+        ASSERT_EQ(0, rc);
+
+        // write data (with commit)
+        WritePattern(handle, 0, (32 - i) * blk, blk, false);
+        ASSERT_FALSE(HasFatalFailure());
+
+        // 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(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, 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(session_, &handle, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // write (without commit)
+    WritePattern(handle, 0, exp_len/2, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // issue some commands that should fail with non-fatal errors
+
+    // write past end of file
+    uint32_t val = 0xDEDBEEF;
+    rc = storage_write(handle,  exp_len/2 + 1, &val, sizeof(val), 0);
+    ASSERT_EQ(-EINVAL, rc);
+
+    // read past end of file
+    rc = storage_read(handle, exp_len/2 + 1, &val, sizeof(val));
+    ASSERT_EQ(-EINVAL, rc);
+
+    // try to extend file past end of file
+    rc = storage_set_file_size(handle, exp_len/2 + 1, 0);
+    ASSERT_EQ(-EINVAL, rc);
+
+    // open non existing file
+    rc = storage_open_file(session_, &handle1, "foo",
+                           STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-ENOENT, rc);
+
+    // delete non-existing file
+    rc = storage_delete_file(session_, "foo", STORAGE_OP_COMPLETE);
+    ASSERT_EQ(-ENOENT, rc);
+
+    // then resume writinga (without commit)
+    WritePattern(handle, exp_len/2, exp_len/2, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // commit current transaction
+    rc = storage_end_transaction(session_, 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
+    ReadPatternEOF(handle, 0, blk, exp_len);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle);
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+// Transaction Collisions
+
+TEST_P(StorageServiceTest, Transact2_WriteNC) {
+    int rc;
+    file_handle_t handle1;
+    file_handle_t handle2;
+    size_t blk = 2048;
+    const char *fname1 = "test_transact_f1";
+    const char *fname2 = "test_transact_f2";
+
+    // open second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    rc = storage_open_file(session_, &handle1, fname1,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    rc = storage_open_file(aux_session_, &handle2, fname2,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // session 1
+    WritePattern(handle1, 0, blk, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read it back
+    ReadPatternEOF(handle1, 0, blk, blk);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // session 2
+    WritePattern(handle2, 0, blk, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read it back
+    ReadPatternEOF(handle2, 0, blk, blk);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle1);
+    storage_close_file(handle2);
+
+    storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);
+    storage_delete_file(aux_session_, fname2, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, Transact2_DeleteNC) {
+    int rc;
+    file_handle_t handle1;
+    file_handle_t handle2;
+    size_t blk = 2048;
+    const char *fname1 = "test_transact_delete_f1";
+    const char *fname2 = "test_transact_delete_f2";
+
+    // open second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    rc = storage_open_file(session_, &handle1, fname1,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    rc = storage_open_file(aux_session_, &handle2, fname2,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // session 1
+    WritePattern(handle1, 0, blk, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read it back
+    ReadPatternEOF(handle1, 0, blk, blk);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // session 2
+    WritePattern(handle2, 0, blk, blk, true);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // read it back
+    ReadPatternEOF(handle2, 0, blk, blk);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // close files and delete them
+    storage_close_file(handle1);
+    storage_delete_file(session_, fname1, 0);
+
+    storage_close_file(handle2);
+    storage_delete_file(aux_session_, fname2, 0);
+
+    // commit
+    rc = storage_end_transaction(session_, true);
+    ASSERT_EQ(0, rc);
+
+    rc = storage_end_transaction(aux_session_, true);
+    ASSERT_EQ(0, rc);
+}
+
+
+TEST_P(StorageServiceTest, Transact2_Write_Read) {
+    int rc;
+    file_handle_t handle1;
+    file_handle_t handle2;
+    size_t blk = 2048;
+    size_t exp_len = 32 * 1024;
+    storage_off_t fsize = (storage_off_t)(-1);
+    const char *fname = "test_transact_writeRead";
+
+    // open second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    // S1: open create truncate file
+    rc = storage_open_file(session_, &handle1, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // S2: open the same file
+    rc = storage_open_file(aux_session_, &handle2, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // S1: write (no commit)
+    WritePattern(handle1, 0, exp_len, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // S1: read it back
+    ReadPatternEOF(handle1, 0, blk, exp_len);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // S2: 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);
+
+    // S2: read it back (should no data)
+    ReadPatternEOF(handle2, 0, blk, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // S1: commit
+    rc = storage_end_transaction(session_, true);
+    ASSERT_EQ(0, rc);
+
+    // S2: check file size, it should fail
+    rc = storage_get_file_size(handle2, &fsize);
+    ASSERT_EQ(-EBUSY, rc);
+
+    // S2: abort transaction
+    rc = storage_end_transaction(aux_session_, false);
+    ASSERT_EQ(0, rc);
+
+    // S2: check file size again, it should be exp_len
+    rc = storage_get_file_size(handle2, &fsize);
+    ASSERT_EQ(0, rc);
+    ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+    // S2: read it again (should be exp_len)
+    ReadPatternEOF(handle2, 0, blk, exp_len);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle1);
+    storage_close_file(handle2);
+
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, Transact2_Write_Write_Commit_Commit) {
+    int rc;
+    file_handle_t handle1;
+    file_handle_t handle2;
+    file_handle_t handle3;
+    size_t blk = 2048;
+    size_t exp_len = 32 * 1024;
+    storage_off_t fsize = (storage_off_t)(-1);
+    const char *fname = "test_transact_write_write_commit_commit";
+
+    // open second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    // S1: open create truncate file
+    rc = storage_open_file(session_, &handle1, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // S2: open the same file
+    rc = storage_open_file(aux_session_, &handle2, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // S1: write (no commit)
+    WritePattern(handle1, 0, exp_len, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // S2: write (no commit)
+    WritePattern(handle2, 0, exp_len/2, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // S1: commit
+    rc = storage_end_transaction(session_, true);
+    ASSERT_EQ(0, rc);
+
+    // S2: read/write/get/set size/delete (all should fail)
+    uint32_t val = 0;
+    rc = storage_read(handle2, 0, &val, sizeof(val));
+    ASSERT_EQ(-EBUSY, rc);
+
+    rc = storage_write(handle2, 0, &val, sizeof(val), 0);
+    ASSERT_EQ(-EBUSY, rc);
+
+    rc = storage_get_file_size(handle2, &fsize);
+    ASSERT_EQ(-EBUSY, rc);
+
+    rc = storage_set_file_size(handle2,  fsize, 0);
+    ASSERT_EQ(-EBUSY, rc);
+
+    rc = storage_delete_file(aux_session_, fname, 0);
+    ASSERT_EQ(-EBUSY, rc);
+
+    rc = storage_open_file(aux_session_, &handle3, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, 0);
+    ASSERT_EQ(-EBUSY, rc);
+
+    // S2: commit (should fail, and failed state should be cleared)
+    rc = storage_end_transaction(aux_session_, true);
+    ASSERT_EQ(-EBUSY, rc);
+
+    // S2: check file size, it should be exp_len
+    rc = storage_get_file_size(handle2, &fsize);
+    ASSERT_EQ(0, rc);
+    ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+    // S2: read it again (should be exp_len)
+    ReadPatternEOF(handle2, 0, blk, exp_len);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle1);
+    storage_close_file(handle2);
+
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, Transact2_Write_Write_Commit_Discard) {
+    int rc;
+    file_handle_t handle1;
+    file_handle_t handle2;
+    file_handle_t handle3;
+    size_t blk = 2048;
+    size_t exp_len = 32 * 1024;
+    storage_off_t fsize = (storage_off_t)(-1);
+    const char *fname = "test_transact_write_write_commit_discard";
+
+    // open second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    // S1: open create truncate file
+    rc = storage_open_file(session_, &handle1, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // S2: open the same file
+    rc = storage_open_file(aux_session_, &handle2, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // S1: write (no commit)
+    WritePattern(handle1, 0, exp_len, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // S2: write (no commit)
+    WritePattern(handle2, 0, exp_len/2, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // S1: commit
+    rc = storage_end_transaction(session_, true);
+    ASSERT_EQ(0, rc);
+
+    // S2: read/write/get/set size/delete (all should fail)
+    uint32_t val = 0;
+    rc = storage_read(handle2, 0, &val, sizeof(val));
+    ASSERT_EQ(-EBUSY, rc);
+
+    rc = storage_write(handle2, 0, &val, sizeof(val), 0);
+    ASSERT_EQ(-EBUSY, rc);
+
+    rc = storage_get_file_size(handle2, &fsize);
+    ASSERT_EQ(-EBUSY, rc);
+
+    rc = storage_set_file_size(handle2,  fsize, 0);
+    ASSERT_EQ(-EBUSY, rc);
+
+    rc = storage_delete_file(aux_session_, fname, 0);
+    ASSERT_EQ(-EBUSY, rc);
+
+    rc = storage_open_file(aux_session_, &handle3, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, 0);
+    ASSERT_EQ(-EBUSY, rc);
+
+    // S2: discard (should fail, and failed state should be cleared)
+    rc = storage_end_transaction(aux_session_, false);
+    ASSERT_EQ(0, rc);
+
+    // S2: check file size, it should be exp_len
+    rc = storage_get_file_size(handle2, &fsize);
+    ASSERT_EQ(0, rc);
+    ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+    // S2: read it again (should be exp_len)
+    ReadPatternEOF(handle2, 0, blk, exp_len);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle1);
+    storage_close_file(handle2);
+
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, Transact2_Write_Write_Discard_Commit) {
+    int rc;
+    file_handle_t handle1;
+    file_handle_t handle2;
+    size_t blk = 2048;
+    size_t exp_len = 32 * 1024;
+    storage_off_t fsize = (storage_off_t)(-1);
+    const char *fname = "test_transact_write_write_discard_commit";
+
+    // open second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    // S1: open create truncate file
+    rc = storage_open_file(session_, &handle1, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // S2: open the same file
+    rc = storage_open_file(aux_session_, &handle2, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // S1: write (no commit)
+    WritePattern(handle1, 0, exp_len, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // S2: write (no commit)
+    WritePattern(handle2, 0, exp_len/2, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // S1: discard
+    rc = storage_end_transaction(session_, false);
+    ASSERT_EQ(0, rc);
+
+    // S2: commit (should succeed)
+    rc = storage_end_transaction(aux_session_, true);
+    ASSERT_EQ(0, rc);
+
+    // S2: check file size, it should be exp_len
+    rc = storage_get_file_size(handle2, &fsize);
+    ASSERT_EQ(0, rc);
+    ASSERT_EQ((storage_off_t)exp_len/2, fsize);
+
+    // S2: read it again (should be exp_len)
+    ReadPatternEOF(handle2, 0, blk, exp_len/2);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle1);
+    storage_close_file(handle2);
+
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, Transact2_Write_Write_Discard_Discard) {
+    int rc;
+    file_handle_t handle1;
+    file_handle_t handle2;
+    size_t blk = 2048;
+    size_t exp_len = 32 * 1024;
+    storage_off_t fsize = (storage_off_t)(-1);
+    const char *fname = "test_transact_write_write_discard_Discard";
+
+    // open second session
+    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+    ASSERT_EQ(0, rc);
+
+    // S1: open create truncate file
+    rc = storage_open_file(session_, &handle1, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // S2: open the same file
+    rc = storage_open_file(aux_session_, &handle2, fname,
+                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+                           STORAGE_OP_COMPLETE);
+    ASSERT_EQ(0, rc);
+
+    // S1: write (no commit)
+    WritePattern(handle1, 0, exp_len, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // S2: write (no commit)
+    WritePattern(handle2, 0, exp_len/2, blk, false);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // S1: discard
+    rc = storage_end_transaction(session_, false);
+    ASSERT_EQ(0, rc);
+
+    // S2: discard
+    rc = storage_end_transaction(aux_session_, false);
+    ASSERT_EQ(0, rc);
+
+    // S2: 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);
+
+    // S2: read it again (should be 0)
+    ReadPatternEOF(handle2, 0, blk, 0);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // cleanup
+    storage_close_file(handle1);
+    storage_close_file(handle2);
+
+    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+