blob: 57b55435f6610ff951bfb14c7681447364a23942 [file] [log] [blame]
// Copyright 2022 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 <fcntl.h>
#include <fidl/fuchsia.feedback.testing/cpp/wire.h>
#include <lib/fdio/vfs.h>
#include <lib/service/llcpp/service.h>
#include <lib/zx/vmo.h>
#include <unistd.h>
#include <cerrno>
#include <fbl/unique_fd.h>
#include <gtest/gtest.h>
#include "src/lib/storage/fs_management/cpp/admin.h"
#include "src/lib/storage/fs_management/cpp/format.h"
#include "src/lib/storage/fs_management/cpp/launch.h"
#include "src/lib/storage/fs_management/cpp/mount.h"
#include "src/lib/storage/fs_management/cpp/options.h"
#include "src/storage/fshost/block-device-manager.h"
#include "src/storage/fshost/block-device.h"
#include "src/storage/fshost/config.h"
#include "src/storage/fshost/constants.h"
#include "src/storage/fshost/testing/fshost_integration_test.h"
#include "src/storage/lib/utils/topological_path.h"
#include "src/storage/minfs/format.h"
#include "src/storage/testing/fvm.h"
#include "src/storage/testing/ram_disk.h"
#include "src/storage/testing/zxcrypt.h"
namespace fshost {
namespace {
constexpr uint32_t kBlockCount = 1024 * 256;
constexpr uint32_t kBlockSize = 512;
constexpr uint32_t kSliceSize = 32'768;
constexpr size_t kDeviceSize = kBlockCount * kBlockSize;
using MigrationTest = testing::FshostIntegrationTest;
TEST_F(MigrationTest, MigratesZxcryptMinfsToFxfs) {
if (DataFilesystemFormat() != "fxfs") {
return;
}
PauseWatcher(); // Pause whilst we create a ramdisk.
// Create a ramdisk with an unformatted minfs partitition.
zx::vmo vmo;
ASSERT_EQ(zx::vmo::create(kDeviceSize, 0, &vmo), ZX_OK);
// Create a child VMO so that we can keep hold of the original.
zx::vmo child_vmo;
ASSERT_EQ(vmo.create_child(ZX_VMO_CHILD_SLICE, 0, kDeviceSize, &child_vmo), ZX_OK);
constexpr char kFileContents[] = "to be, or not to be?";
// Now create the ram-disk with a single FVM partition, formatted with zxcrypt, then minfs.
std::string partition_path_suffix;
{
auto ramdisk_or = storage::RamDisk::CreateWithVmo(std::move(child_vmo), kBlockSize);
ASSERT_EQ(ramdisk_or.status_value(), ZX_OK);
storage::FvmOptions options{
.name = kDataPartitionLabel,
.type = std::array<uint8_t, BLOCK_GUID_LEN>{GUID_DATA_VALUE},
};
auto fvm_partition_or = storage::CreateFvmPartition(ramdisk_or->path(), kSliceSize, options);
ASSERT_EQ(fvm_partition_or.status_value(), ZX_OK);
auto fvm_topological_path_or = storage::GetTopologicalPath(*fvm_partition_or);
ASSERT_EQ(fvm_topological_path_or.status_value(), ZX_OK);
partition_path_suffix = fvm_topological_path_or->substr(ramdisk_or->path().length());
auto zxcrypt_device_path_or = storage::CreateZxcryptVolume(fvm_partition_or.value());
ASSERT_EQ(zxcrypt_device_path_or.status_value(), ZX_OK);
ASSERT_EQ(fs_management::Mkfs(zxcrypt_device_path_or->c_str(),
fs_management::DiskFormat::kDiskFormatMinfs,
fs_management::LaunchStdioSync, fs_management::MkfsOptions{}),
ZX_OK);
// Mount the filesystem and add some data.
auto device_fd = fbl::unique_fd(::open(zxcrypt_device_path_or->c_str(), O_RDONLY));
ASSERT_TRUE(device_fd) << strerror(errno);
auto mount_or =
fs_management::Mount(std::move(device_fd), "/mnt/data", fs_management::kDiskFormatMinfs,
fs_management::MountOptions{}, fs_management::LaunchStdioAsync);
ASSERT_EQ(mount_or.status_value(), ZX_OK);
auto fd = fbl::unique_fd(::open("/mnt/data/file", O_RDWR | O_CREAT));
ASSERT_TRUE(fd) << strerror(errno);
ASSERT_EQ(::write(fd.get(), kFileContents, strlen(kFileContents)),
static_cast<ssize_t>(strlen(kFileContents)));
}
ResumeWatcher();
// Now reattach the ram-disk. Fshost should reformat the filesystem as Fxfs and copy the data
// into it.
auto ramdisk_or = storage::RamDisk::CreateWithVmo(std::move(vmo), kBlockSize);
ASSERT_EQ(ramdisk_or.status_value(), ZX_OK);
// Fxfs should be automatically mounted.
auto [root_fd, fs_type] = WaitForMount("minfs");
EXPECT_TRUE(root_fd);
EXPECT_EQ(fs_type, VFS_TYPE_FXFS);
// The data should have been copied over.
auto fd = fbl::unique_fd(::openat(root_fd.get(), "file", O_RDONLY));
ASSERT_TRUE(fd) << strerror(errno);
char buf[sizeof(kFileContents)] = {0};
ASSERT_EQ(::read(fd.get(), buf, sizeof(buf)), static_cast<ssize_t>(strlen(kFileContents)));
ASSERT_STREQ(buf, kFileContents);
// We should ensure the device isn't zxcrypt-formatted.
std::string device_path = ramdisk_or->path() + partition_path_suffix;
fprintf(stderr, "%s\n", device_path.c_str());
struct stat st;
ASSERT_TRUE(::stat(device_path.c_str(), &st) == 0)
<< "Failed to stat " << device_path << ": " << strerror(errno);
std::string zxcrypt_path = device_path + "/zxcrypt";
ASSERT_FALSE(::stat(zxcrypt_path.c_str(), &st) == 0) << zxcrypt_path << " shouldn't exist.";
// No crash reports should have been filed.
auto client_end = service::Connect<fuchsia_feedback_testing::FakeCrashReporterQuerier>();
ASSERT_EQ(client_end.status_value(), ZX_OK);
auto client = fidl::BindSyncClient(std::move(*client_end));
auto res = client->WatchFile();
ASSERT_EQ(res.status(), ZX_OK);
ASSERT_EQ(res->num_filed, 0ul);
}
} // namespace
} // namespace fshost