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 <utility>
22 #include <vector>
23
24 #include <android-base/unique_fd.h>
25 #include <gmock/gmock.h>
26 #include <gtest/gtest.h>
27 #include <libsnapshot/snapshot_writer.h>
28
29 #include "update_engine/common/utils.h"
30
31 namespace chromeos_update_engine {
32 constexpr size_t BLOCK_SIZE = 4096;
33 constexpr size_t PARTITION_SIZE = BLOCK_SIZE * 10;
34
35 using android::base::unique_fd;
36 using android::snapshot::CompressedSnapshotWriter;
37 using android::snapshot::CowOptions;
38 using android::snapshot::ISnapshotWriter;
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<CompressedSnapshotWriter> GetCowWriter() {
52 const CowOptions options{.block_size = BLOCK_SIZE, .compression = "gz"};
53 auto snapshot_writer = std::make_unique<CompressedSnapshotWriter>(options);
54 int fd = open(cow_device_file_.path().c_str(), O_RDWR);
55 EXPECT_NE(fd, -1);
56 EXPECT_TRUE(snapshot_writer->SetCowDevice(unique_fd{fd}));
57 snapshot_writer->SetSourceDevice(cow_source_file_.path());
58 return snapshot_writer;
59 }
GetCowFd()60 CowWriterFileDescriptor GetCowFd() {
61 auto cow_writer = GetCowWriter();
62 return CowWriterFileDescriptor{std::move(cow_writer)};
63 }
64
65 ScopedTempFile cow_source_file_{"cow_source.XXXXXX", true};
66 ScopedTempFile cow_device_file_{"cow_device.XXXXXX", true};
67 };
68
TEST_F(CowWriterFileDescriptorUnittest,ReadAfterWrite)69 TEST_F(CowWriterFileDescriptorUnittest, ReadAfterWrite) {
70 std::vector<unsigned char> buffer;
71 buffer.resize(BLOCK_SIZE);
72 std::fill(buffer.begin(), buffer.end(), 234);
73
74 std::vector<unsigned char> verity_data;
75 verity_data.resize(BLOCK_SIZE);
76 std::fill(verity_data.begin(), verity_data.end(), 0xAA);
77
78 auto cow_writer = GetCowWriter();
79 cow_writer->Initialize();
80
81 // Simulate Writing InstallOp data
82 ASSERT_TRUE(cow_writer->AddRawBlocks(0, buffer.data(), buffer.size()));
83 ASSERT_TRUE(cow_writer->AddZeroBlocks(1, 2));
84 ASSERT_TRUE(cow_writer->AddCopy(3, 1));
85 // Fake label to simulate "end of install"
86 ASSERT_TRUE(cow_writer->AddLabel(23));
87 ASSERT_TRUE(
88 cow_writer->AddRawBlocks(4, verity_data.data(), verity_data.size()));
89 ASSERT_TRUE(cow_writer->Finalize());
90
91 cow_writer = GetCowWriter();
92 ASSERT_NE(nullptr, cow_writer);
93 ASSERT_TRUE(cow_writer->InitializeAppend(23));
94 auto cow_fd =
95 std::make_unique<CowWriterFileDescriptor>(std::move(cow_writer));
96
97 ASSERT_EQ((ssize_t)BLOCK_SIZE * 4, cow_fd->Seek(BLOCK_SIZE * 4, SEEK_SET));
98 std::vector<unsigned char> read_back(4096);
99 ASSERT_EQ((ssize_t)read_back.size(),
100 cow_fd->Read(read_back.data(), read_back.size()));
101 ASSERT_EQ(verity_data, read_back);
102
103 // Since we didn't write anything to this instance of cow_fd, destructor
104 // should not call Finalize(). As finalize will drop ops after resume label,
105 // causing subsequent reads to fail.
106 cow_writer = GetCowWriter();
107 ASSERT_NE(nullptr, cow_writer);
108 ASSERT_TRUE(cow_writer->InitializeAppend(23));
109 cow_fd = std::make_unique<CowWriterFileDescriptor>(std::move(cow_writer));
110
111 ASSERT_EQ((ssize_t)BLOCK_SIZE * 4, cow_fd->Seek(BLOCK_SIZE * 4, SEEK_SET));
112 ASSERT_EQ((ssize_t)read_back.size(),
113 cow_fd->Read(read_back.data(), read_back.size()));
114 ASSERT_EQ(verity_data, read_back)
115 << "Could not read verity data afeter InitializeAppend() => Read() => "
116 "InitializeAppend() sequence. If no writes happened while CowWriterFd "
117 "is open, Finalize() should not be called.";
118 }
119
120 } // namespace chromeos_update_engine
121