1 // Copyright (C) 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <unordered_set>
16 
17 #include <android-base/file.h>
18 #include <gtest/gtest.h>
19 #include <libsnapshot/cow_writer.h>
20 #include <payload_consumer/file_descriptor.h>
21 
22 namespace android {
23 namespace snapshot {
24 
25 using android::base::unique_fd;
26 using chromeos_update_engine::FileDescriptor;
27 
28 static constexpr uint32_t kBlockSize = 4096;
29 static constexpr size_t kBlockCount = 10;
30 
31 class OfflineSnapshotTest : public ::testing::Test {
32   protected:
SetUp()33     virtual void SetUp() override {
34         base_ = std::make_unique<TemporaryFile>();
35         ASSERT_GE(base_->fd, 0) << strerror(errno);
36 
37         cow_ = std::make_unique<TemporaryFile>();
38         ASSERT_GE(cow_->fd, 0) << strerror(errno);
39 
40         WriteBaseDevice();
41     }
42 
TearDown()43     virtual void TearDown() override {
44         base_ = nullptr;
45         cow_ = nullptr;
46         base_blocks_ = {};
47     }
48 
WriteBaseDevice()49     void WriteBaseDevice() {
50         unique_fd random(open("/dev/urandom", O_RDONLY));
51         ASSERT_GE(random, 0);
52 
53         for (size_t i = 0; i < kBlockCount; i++) {
54             std::string block(kBlockSize, 0);
55             ASSERT_TRUE(android::base::ReadFully(random, block.data(), block.size()));
56             ASSERT_TRUE(android::base::WriteFully(base_->fd, block.data(), block.size()));
57             base_blocks_.emplace_back(std::move(block));
58         }
59         ASSERT_EQ(fsync(base_->fd), 0);
60     }
61 
WriteCow(ICowWriter * writer)62     void WriteCow(ICowWriter* writer) {
63         std::string new_block = MakeNewBlockString();
64         std::string xor_block = MakeXorBlockString();
65 
66         ASSERT_TRUE(writer->AddXorBlocks(1, xor_block.data(), xor_block.size(), 0, kBlockSize / 2));
67         ASSERT_TRUE(writer->AddCopy(3, 0));
68         ASSERT_TRUE(writer->AddRawBlocks(5, new_block.data(), new_block.size()));
69         ASSERT_TRUE(writer->AddZeroBlocks(7, 2));
70         ASSERT_TRUE(writer->Finalize());
71     }
72 
TestBlockReads(ICowWriter * writer)73     void TestBlockReads(ICowWriter* writer) {
74         auto reader = writer->OpenFileDescriptor(base_->path);
75         ASSERT_NE(reader, nullptr);
76 
77         // Test that unchanged blocks are not modified.
78         std::unordered_set<size_t> changed_blocks = {1, 3, 5, 7, 8};
79         for (size_t i = 0; i < kBlockCount; i++) {
80             if (changed_blocks.count(i)) {
81                 continue;
82             }
83 
84             std::string block(kBlockSize, 0);
85             ASSERT_EQ(reader->Seek(i * kBlockSize, SEEK_SET), i * kBlockSize);
86             ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
87             ASSERT_EQ(block, base_blocks_[i]);
88         }
89 
90         // Test that we can read back our modified blocks.
91         std::string data(kBlockSize, 0);
92         std::string offsetblock = base_blocks_[0].substr(kBlockSize / 2, kBlockSize / 2) +
93                                   base_blocks_[1].substr(0, kBlockSize / 2);
94         ASSERT_EQ(offsetblock.size(), kBlockSize);
95         ASSERT_EQ(reader->Seek(1 * kBlockSize, SEEK_SET), 1 * kBlockSize);
96         ASSERT_EQ(reader->Read(data.data(), data.size()), kBlockSize);
97         for (int i = 0; i < 100; i++) {
98             data[i] = (char)~(data[i]);
99         }
100         ASSERT_EQ(data, offsetblock);
101 
102         std::string block(kBlockSize, 0);
103         ASSERT_EQ(reader->Seek(3 * kBlockSize, SEEK_SET), 3 * kBlockSize);
104         ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
105         ASSERT_EQ(block, base_blocks_[0]);
106 
107         ASSERT_EQ(reader->Seek(5 * kBlockSize, SEEK_SET), 5 * kBlockSize);
108         ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
109         ASSERT_EQ(block, MakeNewBlockString());
110 
111         std::string two_blocks(kBlockSize * 2, 0x7f);
112         std::string zeroes(kBlockSize * 2, 0);
113         ASSERT_EQ(reader->Seek(7 * kBlockSize, SEEK_SET), 7 * kBlockSize);
114         ASSERT_EQ(reader->Read(two_blocks.data(), two_blocks.size()), two_blocks.size());
115         ASSERT_EQ(two_blocks, zeroes);
116     }
117 
TestByteReads(ICowWriter * writer)118     void TestByteReads(ICowWriter* writer) {
119         auto reader = writer->OpenFileDescriptor(base_->path);
120         ASSERT_NE(reader, nullptr);
121 
122         std::string blob(kBlockSize * 3, 'x');
123 
124         // Test that we can read in the middle of a block.
125         static constexpr size_t kOffset = 970;
126         off64_t offset = 3 * kBlockSize + kOffset;
127         ASSERT_EQ(reader->Seek(0, SEEK_SET), 0);
128         ASSERT_EQ(reader->Seek(offset, SEEK_CUR), offset);
129         ASSERT_EQ(reader->Read(blob.data(), blob.size()), blob.size());
130         ASSERT_EQ(blob.substr(0, 100), base_blocks_[0].substr(kOffset, 100));
131         ASSERT_EQ(blob.substr(kBlockSize - kOffset, kBlockSize), base_blocks_[4]);
132         ASSERT_EQ(blob.substr(kBlockSize * 2 - kOffset, 100), MakeNewBlockString().substr(0, 100));
133         ASSERT_EQ(blob.substr(blob.size() - kOffset), base_blocks_[6].substr(0, kOffset));
134 
135         // Pull a random byte from the compressed block.
136         char value;
137         offset = 5 * kBlockSize + 1000;
138         ASSERT_EQ(reader->Seek(offset, SEEK_SET), offset);
139         ASSERT_EQ(reader->Read(&value, sizeof(value)), sizeof(value));
140         ASSERT_EQ(value, MakeNewBlockString()[1000]);
141 
142         // Test a sequence of one byte reads.
143         offset = 5 * kBlockSize + 10;
144         std::string expected = MakeNewBlockString().substr(10, 20);
145         ASSERT_EQ(reader->Seek(offset, SEEK_SET), offset);
146 
147         std::string got;
148         while (got.size() < expected.size()) {
149             ASSERT_EQ(reader->Read(&value, sizeof(value)), sizeof(value));
150             got.push_back(value);
151         }
152         ASSERT_EQ(got, expected);
153     }
154 
TestReads(ICowWriter * writer)155     void TestReads(ICowWriter* writer) {
156         ASSERT_NO_FATAL_FAILURE(TestBlockReads(writer));
157         ASSERT_NO_FATAL_FAILURE(TestByteReads(writer));
158     }
159 
MakeNewBlockString()160     std::string MakeNewBlockString() {
161         std::string new_block = "This is a new block";
162         new_block.resize(kBlockSize / 2, '*');
163         new_block.resize(kBlockSize, '!');
164         return new_block;
165     }
166 
MakeXorBlockString()167     std::string MakeXorBlockString() {
168         std::string data(kBlockSize, 0);
169         memset(data.data(), 0xff, 100);
170         return data;
171     }
172 
173     std::unique_ptr<TemporaryFile> base_;
174     std::unique_ptr<TemporaryFile> cow_;
175     std::vector<std::string> base_blocks_;
176 };
177 
TEST_F(OfflineSnapshotTest,CompressedSnapshot)178 TEST_F(OfflineSnapshotTest, CompressedSnapshot) {
179     CowOptions options;
180     options.compression = "gz";
181     options.max_blocks = {kBlockCount};
182     options.scratch_space = false;
183 
184     unique_fd cow_fd(dup(cow_->fd));
185     ASSERT_GE(cow_fd, 0);
186 
187     auto writer = CreateCowWriter(2, options, std::move(cow_fd));
188     ASSERT_NO_FATAL_FAILURE(WriteCow(writer.get()));
189     ASSERT_NO_FATAL_FAILURE(TestReads(writer.get()));
190 }
191 
192 }  // namespace snapshot
193 }  // namespace android
194