1 //
2 // Copyright (C) 2021 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "update_engine/payload_consumer/cow_writer_file_descriptor.h"
18
19 #include <cstring>
20 #include <memory>
21 #include <string>
22 #include <utility>
23 #include <vector>
24
25 #include <android-base/unique_fd.h>
26 #include <gmock/gmock.h>
27 #include <gtest/gtest.h>
28 #include <libsnapshot/cow_writer.h>
29
30 #include "update_engine/common/utils.h"
31
32 namespace chromeos_update_engine {
33 constexpr size_t BLOCK_SIZE = 4096;
34 constexpr size_t PARTITION_SIZE = BLOCK_SIZE * 10;
35
36 using android::base::unique_fd;
37 using android::snapshot::CowOptions;
38 using android::snapshot::ICowWriter;
39
40 class CowWriterFileDescriptorUnittest : public ::testing::Test {
41 public:
SetUp()42 void SetUp() override {
43 ASSERT_EQ(ftruncate64(cow_device_file_.fd(), PARTITION_SIZE), 0)
44 << "Failed to truncate cow_device file to " << PARTITION_SIZE
45 << strerror(errno);
46 ASSERT_EQ(ftruncate64(cow_source_file_.fd(), PARTITION_SIZE), 0)
47 << "Failed to truncate cow_source file to " << PARTITION_SIZE
48 << strerror(errno);
49 }
50
GetCowWriter()51 std::unique_ptr<ICowWriter> GetCowWriter() {
52 static constexpr uint32_t kTestCowVersion = 2;
53 const CowOptions options{.block_size = BLOCK_SIZE, .compression = "gz"};
54 int fd = open(cow_device_file_.path().c_str(), O_RDWR);
55 EXPECT_NE(fd, -1);
56 return android::snapshot::CreateCowWriter(
57 kTestCowVersion, options, unique_fd{fd});
58 }
GetCowFd()59 std::unique_ptr<CowWriterFileDescriptor> GetCowFd() {
60 auto cow_writer = GetCowWriter();
61 EXPECT_NE(cow_writer, nullptr);
62 auto fd = cow_writer->OpenFileDescriptor({cow_source_file_.path()});
63 EXPECT_NE(fd, nullptr);
64 auto source_path = std::optional<std::string>{cow_source_file_.path()};
65 return std::make_unique<CowWriterFileDescriptor>(
66 std::move(cow_writer), std::move(fd), source_path);
67 }
68
69 ScopedTempFile cow_source_file_{"cow_source.XXXXXX", true};
70 ScopedTempFile cow_device_file_{"cow_device.XXXXXX", true};
71 };
72
TEST_F(CowWriterFileDescriptorUnittest,ReadAfterWrite)73 TEST_F(CowWriterFileDescriptorUnittest, ReadAfterWrite) {
74 std::vector<unsigned char> buffer;
75 buffer.resize(BLOCK_SIZE);
76 std::fill(buffer.begin(), buffer.end(), 234);
77
78 std::vector<unsigned char> verity_data;
79 verity_data.resize(BLOCK_SIZE);
80 std::fill(verity_data.begin(), verity_data.end(), 0xAA);
81
82 auto cow_writer = GetCowWriter();
83
84 // Simulate Writing InstallOp data
85 ASSERT_TRUE(cow_writer->AddRawBlocks(0, buffer.data(), buffer.size()));
86 ASSERT_TRUE(cow_writer->AddZeroBlocks(1, 2));
87 ASSERT_TRUE(cow_writer->AddCopy(3, 1));
88 // Fake label to simulate "end of install"
89 ASSERT_TRUE(cow_writer->AddLabel(23));
90 ASSERT_TRUE(
91 cow_writer->AddRawBlocks(4, verity_data.data(), verity_data.size()));
92 ASSERT_TRUE(cow_writer->Finalize());
93
94 auto cow_fd = GetCowFd();
95
96 ASSERT_EQ((ssize_t)BLOCK_SIZE * 4, cow_fd->Seek(BLOCK_SIZE * 4, SEEK_SET));
97 std::vector<unsigned char> read_back(4096);
98 ASSERT_EQ((ssize_t)read_back.size(),
99 cow_fd->Read(read_back.data(), read_back.size()));
100 ASSERT_EQ(verity_data, read_back);
101
102 // Since we didn't write anything to this instance of cow_fd, destructor
103 // should not call Finalize(). As finalize will drop ops after resume label,
104 // causing subsequent reads to fail.
105 cow_fd = GetCowFd();
106
107 ASSERT_EQ((ssize_t)BLOCK_SIZE * 4, cow_fd->Seek(BLOCK_SIZE * 4, SEEK_SET));
108 ASSERT_EQ((ssize_t)read_back.size(),
109 cow_fd->Read(read_back.data(), read_back.size()));
110 ASSERT_EQ(verity_data, read_back)
111 << "Could not read verity data after InitializeAppend() => Read() => "
112 "InitializeAppend() sequence. If no writes happened while CowWriterFd "
113 "is open, Finalize() should not be called.";
114 }
115
116 } // namespace chromeos_update_engine
117