blob: 2bf8588f92cc2cd12390a652f95fd2d01ed7724a [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/fdio.h>
#include <blobfs/mkfs.h>
#include <blobfs/mount.h>
#include <block-client/cpp/block-device.h>
#include <block-client/cpp/fake-device.h>
#include <zxtest/zxtest.h>
#include "blobfs.h"
#include "fdio_test.h"
#include "nand_test.h"
#include "runner.h"
#include "test/blob_utils.h"
namespace blobfs {
namespace {
using SyncFdioTest = FdioTest;
using SyncNandTest = NandTest;
uint64_t GetSucceededFlushCalls(block_client::FakeBlockDevice* device) {
fuchsia_hardware_block_BlockStats stats;
device->GetStats(true, &stats);
return stats.flush.success.total_calls;
}
} // namespace
// Verifies that fdio "fsync" calls actually sync blobfs files to the block device and verifies
// behavior for different lifecycles of creating a file.
TEST_F(SyncFdioTest, Sync) {
std::unique_ptr<BlobInfo> info;
ASSERT_NO_FAILURES(GenerateRandomBlob("", 64, &info));
memmove(info->path, info->path + 1, strlen(info->path)); // Remove leading slash.
int file = openat(root_fd(), info->path, O_RDWR | O_CREAT);
EXPECT_TRUE(file >= 1);
// We have not written any data to the file. Blobfs requires the file data to be written so the
// name is the hash of the contents.
EXPECT_EQ(-1, fsync(file));
// Write the contents. The file must be truncated before writing to declare its size.
EXPECT_EQ(0, ftruncate(file, info->size_data));
EXPECT_EQ(info->size_data, write(file, info->data.get(), info->size_data));
// Sync the file. This will block until woken up by the file_wake_thread.
EXPECT_EQ(0, fsync(file));
// fsync on a file will flush the writes to the block device but not actually flush the block
// device itself.
fuchsia_hardware_block_BlockStats stats;
block_device()->GetStats(true, &stats);
EXPECT_LE(1u, stats.write.success.total_calls);
EXPECT_EQ(0u, stats.flush.success.total_calls);
// Sync the root directory. Syncing a directory will force the block device to flush.
EXPECT_EQ(0, fsync(root_fd()));
EXPECT_EQ(1u, GetSucceededFlushCalls(block_device()));
}
// Verifies that fdio "sync" actually flushes a NAND device. This tests the fdio, blobfs, block
// device, and FTL layers.
TEST_F(SyncNandTest, Sync) {
// Make a VMO to give to the RAM-NAND.
const size_t vmo_size = Connection::GetVMOSize();
zx::vmo initial_vmo;
ASSERT_OK(zx::vmo::create(vmo_size, 0, &initial_vmo));
// NAND VMOs must be prepopulated with 0xff.
zx_vaddr_t vmar_address = 0;
ASSERT_OK(zx::vmar::root_self()->map(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, initial_vmo, 0, vmo_size,
&vmar_address));
char* initial_vmo_data = reinterpret_cast<char*>(vmar_address);
std::fill(initial_vmo_data, &initial_vmo_data[vmo_size], 0xff);
// Create a second VMO for later use.
zx::vmo second_vmo;
ASSERT_OK(zx::vmo::create(vmo_size, 0, &second_vmo));
ASSERT_OK(zx::vmar::root_self()->map(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, second_vmo, 0, vmo_size,
&vmar_address));
char* second_vmo_data = reinterpret_cast<char*>(vmar_address);
std::unique_ptr<BlobInfo> info;
ASSERT_NO_FAILURES(GenerateRandomBlob("", 64, &info));
{
Connection initial_connection("/initial/dev", std::move(initial_vmo), true);
memmove(info->path, info->path + 1, strlen(info->path)); // Remove leading slash.
fbl::unique_fd file(openat(initial_connection.root_fd(), info->path, O_RDWR | O_CREAT));
ASSERT_TRUE(file.is_valid());
// Write the contents. The file must be truncated before writing to declare its size.
ASSERT_EQ(0, ftruncate(file.get(), info->size_data));
ASSERT_EQ(info->size_data, write(file.get(), info->data.get(), info->size_data));
// This should block until the sync is complete. fsync-ing the root FD is required to flush
// everything.
ASSERT_EQ(0, fsync(file.get()));
ASSERT_EQ(0, fsync(initial_connection.root_fd()));
// Without closing the file or tearing down the existing connection (which may add extra
// flushes, etc.), create a snapshot of the current memory in the second VMO. This will emulate
// a power cycle
memcpy(second_vmo_data, initial_vmo_data, vmo_size);
}
// New connection with a completely new NAND controller reading the same memory.
//
// This call may fail if the above fsync on the root directory is not successful because the
// device will have garbage data in it.
Connection second_connection("/second/dev", std::move(second_vmo), false);
// The blob file should exist.
fbl::unique_fd file(openat(second_connection.root_fd(), info->path, O_RDONLY));
ASSERT_TRUE(file.is_valid());
// The contents should be exactly what we wrote.
std::unique_ptr<char[]> read_data = std::make_unique<char[]>(info->size_data);
ASSERT_EQ(info->size_data, read(file.get(), read_data.get(), info->size_data));
EXPECT_BYTES_EQ(info->data.get(), &read_data[0], info->size_data, "mismatch");
}
} // namespace blobfs