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