[paver] Don't attempt to bind FVM if it's already bound.
This eliminates a devhost deadlock which occurs when attempting to wait
for bind to finish.
Tested: Paved astro, NUC, and vim2
Change-Id: I97a8c61ea9f52b7d9c74320ba744113593bd541f
diff --git a/zircon/system/ulib/paver/BUILD.gn b/zircon/system/ulib/paver/BUILD.gn
index 4ee2857..ef45b15 100644
--- a/zircon/system/ulib/paver/BUILD.gn
+++ b/zircon/system/ulib/paver/BUILD.gn
@@ -55,6 +55,7 @@
output_name = "paver-test"
sources = [
"test/device-partitioner-test.cpp",
+ "test/fvm-test.cpp",
"test/paversvc-test.cpp",
"test/stream-reader-test.cpp",
"test/test-utils.cpp",
diff --git a/zircon/system/ulib/paver/include/lib/paver/paver.h b/zircon/system/ulib/paver/include/lib/paver/paver.h
index 994366e..a35423c 100644
--- a/zircon/system/ulib/paver/include/lib/paver/paver.h
+++ b/zircon/system/ulib/paver/include/lib/paver/paver.h
@@ -13,6 +13,18 @@
namespace paver {
+// Options for locating an FVM within a partition.
+enum class BindOption {
+ // Bind to the FVM, if it exists already.
+ TryBind,
+ // Reformat the partition, regardless of if it already exists as an FVM.
+ Reformat,
+};
+
+// Public for testing.
+fbl::unique_fd FvmPartitionFormat(fbl::unique_fd partition_fd, size_t slice_size,
+ BindOption option);
+
class Paver {
public:
// Writes a kernel or verified boot metadata payload to the appropriate
diff --git a/zircon/system/ulib/paver/paver.cpp b/zircon/system/ulib/paver/paver.cpp
index 9db609b..651bcc9 100644
--- a/zircon/system/ulib/paver/paver.cpp
+++ b/zircon/system/ulib/paver/paver.cpp
@@ -393,6 +393,16 @@
return fbl::unique_fd();
}
+ // We assume the FVM will either have completed binding, or is not bound at all. This is ensured
+ // by the paver always waiting for the FVM to bind after invoking ControllerBind.
+ char fvm_path[PATH_MAX];
+ snprintf(fvm_path, sizeof(fvm_path), "%s/fvm", path);
+
+ fbl::unique_fd fvm(open(fvm_path, O_RDWR));
+ if (fvm) {
+ return fvm;
+ }
+
fdio_t* io = fdio_unsafe_fd_to_io(partition_fd.get());
if (io == nullptr) {
ERROR("Failed to convert to io\n");
@@ -411,8 +421,6 @@
return fbl::unique_fd();
}
- char fvm_path[PATH_MAX];
- snprintf(fvm_path, sizeof(fvm_path), "%s/fvm", path);
if (wait_for_device(fvm_path, timeout.get()) != ZX_OK) {
ERROR("Error waiting for fvm driver to bind\n");
return fbl::unique_fd();
@@ -420,13 +428,7 @@
return fbl::unique_fd(open(fvm_path, O_RDWR));
}
-// Options for locating an FVM within a partition.
-enum class BindOption {
- // Bind to the FVM, if it exists already.
- TryBind,
- // Reformat the partition, regardless of if it already exists as an FVM.
- Reformat,
-};
+} // namespace
// Formats the FVM within the provided partition if it is not already formatted.
//
@@ -487,6 +489,8 @@
return TryBindToFvmDriver(partition_fd, zx::sec(3));
}
+namespace {
+
// Formats a block device as a zxcrypt volume.
//
// On success, returns a file descriptor to an FVM.
@@ -1018,8 +1022,8 @@
memset(buffer.get(), 0, remaining_bytes);
status = payload_vmo.write(buffer.get(), payload_size, remaining_bytes);
if (status != ZX_OK) {
- ERROR("Failed to write padding to vmo\n");
- return status;
+ ERROR("Failed to write padding to vmo\n");
+ return status;
}
payload_size += remaining_bytes;
}
@@ -1084,7 +1088,7 @@
}
zx_status_t Paver::WriteAsset(fuchsia_paver_Configuration configuration, fuchsia_paver_Asset asset,
- const fuchsia_mem_Buffer& payload) {
+ const fuchsia_mem_Buffer& payload) {
if (!InitializePartitioner()) {
return ZX_ERR_BAD_STATE;
}
diff --git a/zircon/system/ulib/paver/test/fvm-test.cpp b/zircon/system/ulib/paver/test/fvm-test.cpp
new file mode 100644
index 0000000..debcaaf
--- /dev/null
+++ b/zircon/system/ulib/paver/test/fvm-test.cpp
@@ -0,0 +1,66 @@
+// Copyright 2019 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/paver/paver.h>
+
+#include <fs-management/fvm.h>
+#include <zxtest/zxtest.h>
+
+#include "test/test-utils.h"
+
+namespace {
+
+constexpr size_t kSliceSize = kBlockSize * 2;
+constexpr uint8_t kFvmType[GPT_GUID_LEN] = GUID_FVM_VALUE;
+
+} // namespace
+
+class FvmTest : public zxtest::Test {
+public:
+ FvmTest() {
+ BlockDevice::Create(kFvmType, &device_);
+ ASSERT_TRUE(device_);
+ }
+
+ int borrow_fd() {
+ return device_->fd();
+ }
+
+ fbl::unique_fd fd() {
+ return fbl::unique_fd(dup(device_->fd()));
+ }
+
+private:
+ std::unique_ptr<BlockDevice> device_;
+};
+
+TEST_F(FvmTest, FormatFvmEmpty) {
+ fbl::unique_fd fvm_part = FvmPartitionFormat(fd(), kSliceSize, paver::BindOption::Reformat);
+ ASSERT_TRUE(fvm_part.is_valid());
+}
+
+TEST_F(FvmTest, TryBindEmpty) {
+ fbl::unique_fd fvm_part = FvmPartitionFormat(fd(), kSliceSize, paver::BindOption::TryBind);
+ ASSERT_TRUE(fvm_part.is_valid());
+}
+
+TEST_F(FvmTest, TryBindAlreadyFormatted) {
+ ASSERT_OK(fvm_init(borrow_fd(), kSliceSize));
+ fbl::unique_fd fvm_part = FvmPartitionFormat(fd(), kSliceSize, paver::BindOption::TryBind);
+ ASSERT_TRUE(fvm_part.is_valid());
+}
+
+TEST_F(FvmTest, TryBindAlreadyBound) {
+ fbl::unique_fd fvm_part = FvmPartitionFormat(fd(), kSliceSize, paver::BindOption::Reformat);
+ ASSERT_TRUE(fvm_part.is_valid());
+
+ fvm_part = FvmPartitionFormat(fd(), kSliceSize, paver::BindOption::TryBind);
+ ASSERT_TRUE(fvm_part.is_valid());
+}
+
+TEST_F(FvmTest, TryBindAlreadyFormattedWrongSliceSize) {
+ ASSERT_OK(fvm_init(borrow_fd(), kSliceSize * 2));
+ fbl::unique_fd fvm_part = FvmPartitionFormat(fd(), kSliceSize, paver::BindOption::TryBind);
+ ASSERT_TRUE(fvm_part.is_valid());
+}
diff --git a/zircon/system/ulib/paver/test/test-utils.h b/zircon/system/ulib/paver/test/test-utils.h
index 903782d..a38a7de 100644
--- a/zircon/system/ulib/paver/test/test-utils.h
+++ b/zircon/system/ulib/paver/test/test-utils.h
@@ -13,7 +13,7 @@
#include <zxtest/zxtest.h>
constexpr uint64_t kBlockSize = 0x1000;
-constexpr uint64_t kBlockCount = 0x10;
+constexpr uint64_t kBlockCount = 0x100;
constexpr uint32_t kOobSize = 8;
constexpr uint32_t kPageSize = 1024;
@@ -33,6 +33,9 @@
ramdisk_destroy(client_);
}
+ // Does not transfer ownership of the file descriptor.
+ int fd() { return ramdisk_get_block_fd(client_); }
+
private:
BlockDevice(ramdisk_client_t* client)
: client_(client) {}