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 <libsnapshot/snapshot.h>
16
17 #include <unordered_set>
18
19 #include <android-base/file.h>
20 #include <gtest/gtest.h>
21 #include <libsnapshot/cow_writer.h>
22 #include <payload_consumer/file_descriptor.h>
23
24 namespace android {
25 namespace snapshot {
26
27 using android::base::unique_fd;
28 using chromeos_update_engine::FileDescriptor;
29
30 static constexpr uint32_t kBlockSize = 4096;
31 static constexpr size_t kBlockCount = 10;
32
33 class OfflineSnapshotTest : public ::testing::Test {
34 protected:
SetUp()35 virtual void SetUp() override {
36 base_ = std::make_unique<TemporaryFile>();
37 ASSERT_GE(base_->fd, 0) << strerror(errno);
38
39 cow_ = std::make_unique<TemporaryFile>();
40 ASSERT_GE(cow_->fd, 0) << strerror(errno);
41
42 WriteBaseDevice();
43 }
44
TearDown()45 virtual void TearDown() override {
46 base_ = nullptr;
47 cow_ = nullptr;
48 base_blocks_ = {};
49 }
50
WriteBaseDevice()51 void WriteBaseDevice() {
52 unique_fd random(open("/dev/urandom", O_RDONLY));
53 ASSERT_GE(random, 0);
54
55 for (size_t i = 0; i < kBlockCount; i++) {
56 std::string block(kBlockSize, 0);
57 ASSERT_TRUE(android::base::ReadFully(random, block.data(), block.size()));
58 ASSERT_TRUE(android::base::WriteFully(base_->fd, block.data(), block.size()));
59 base_blocks_.emplace_back(std::move(block));
60 }
61 ASSERT_EQ(fsync(base_->fd), 0);
62 }
63
WriteCow(ISnapshotWriter * writer)64 void WriteCow(ISnapshotWriter* writer) {
65 std::string new_block = MakeNewBlockString();
66 std::string xor_block = MakeXorBlockString();
67
68 ASSERT_TRUE(writer->AddXorBlocks(1, xor_block.data(), xor_block.size(), 0, kBlockSize / 2));
69 ASSERT_TRUE(writer->AddCopy(3, 0));
70 ASSERT_TRUE(writer->AddRawBlocks(5, new_block.data(), new_block.size()));
71 ASSERT_TRUE(writer->AddZeroBlocks(7, 2));
72 ASSERT_TRUE(writer->Finalize());
73 }
74
TestBlockReads(ISnapshotWriter * writer)75 void TestBlockReads(ISnapshotWriter* writer) {
76 auto reader = writer->OpenReader();
77 ASSERT_NE(reader, nullptr);
78
79 // Test that unchanged blocks are not modified.
80 std::unordered_set<size_t> changed_blocks = {1, 3, 5, 7, 8};
81 for (size_t i = 0; i < kBlockCount; i++) {
82 if (changed_blocks.count(i)) {
83 continue;
84 }
85
86 std::string block(kBlockSize, 0);
87 ASSERT_EQ(reader->Seek(i * kBlockSize, SEEK_SET), i * kBlockSize);
88 ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
89 ASSERT_EQ(block, base_blocks_[i]);
90 }
91
92 // Test that we can read back our modified blocks.
93 std::string data(kBlockSize, 0);
94 std::string offsetblock = base_blocks_[0].substr(kBlockSize / 2, kBlockSize / 2) +
95 base_blocks_[1].substr(0, kBlockSize / 2);
96 ASSERT_EQ(offsetblock.size(), kBlockSize);
97 ASSERT_EQ(reader->Seek(1 * kBlockSize, SEEK_SET), 1 * kBlockSize);
98 ASSERT_EQ(reader->Read(data.data(), data.size()), kBlockSize);
99 for (int i = 0; i < 100; i++) {
100 data[i] = (char)~(data[i]);
101 }
102 ASSERT_EQ(data, offsetblock);
103
104 std::string block(kBlockSize, 0);
105 ASSERT_EQ(reader->Seek(3 * kBlockSize, SEEK_SET), 3 * kBlockSize);
106 ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
107 ASSERT_EQ(block, base_blocks_[0]);
108
109 ASSERT_EQ(reader->Seek(5 * kBlockSize, SEEK_SET), 5 * kBlockSize);
110 ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
111 ASSERT_EQ(block, MakeNewBlockString());
112
113 std::string two_blocks(kBlockSize * 2, 0x7f);
114 std::string zeroes(kBlockSize * 2, 0);
115 ASSERT_EQ(reader->Seek(7 * kBlockSize, SEEK_SET), 7 * kBlockSize);
116 ASSERT_EQ(reader->Read(two_blocks.data(), two_blocks.size()), two_blocks.size());
117 ASSERT_EQ(two_blocks, zeroes);
118 }
119
TestByteReads(ISnapshotWriter * writer)120 void TestByteReads(ISnapshotWriter* writer) {
121 auto reader = writer->OpenReader();
122 ASSERT_NE(reader, nullptr);
123
124 std::string blob(kBlockSize * 3, 'x');
125
126 // Test that we can read in the middle of a block.
127 static constexpr size_t kOffset = 970;
128 off64_t offset = 3 * kBlockSize + kOffset;
129 ASSERT_EQ(reader->Seek(0, SEEK_SET), 0);
130 ASSERT_EQ(reader->Seek(offset, SEEK_CUR), offset);
131 ASSERT_EQ(reader->Read(blob.data(), blob.size()), blob.size());
132 ASSERT_EQ(blob.substr(0, 100), base_blocks_[0].substr(kOffset, 100));
133 ASSERT_EQ(blob.substr(kBlockSize - kOffset, kBlockSize), base_blocks_[4]);
134 ASSERT_EQ(blob.substr(kBlockSize * 2 - kOffset, 100), MakeNewBlockString().substr(0, 100));
135 ASSERT_EQ(blob.substr(blob.size() - kOffset), base_blocks_[6].substr(0, kOffset));
136
137 // Pull a random byte from the compressed block.
138 char value;
139 offset = 5 * kBlockSize + 1000;
140 ASSERT_EQ(reader->Seek(offset, SEEK_SET), offset);
141 ASSERT_EQ(reader->Read(&value, sizeof(value)), sizeof(value));
142 ASSERT_EQ(value, MakeNewBlockString()[1000]);
143 }
144
TestReads(ISnapshotWriter * writer)145 void TestReads(ISnapshotWriter* writer) {
146 ASSERT_NO_FATAL_FAILURE(TestBlockReads(writer));
147 ASSERT_NO_FATAL_FAILURE(TestByteReads(writer));
148 }
149
MakeNewBlockString()150 std::string MakeNewBlockString() {
151 std::string new_block = "This is a new block";
152 new_block.resize(kBlockSize / 2, '*');
153 new_block.resize(kBlockSize, '!');
154 return new_block;
155 }
156
MakeXorBlockString()157 std::string MakeXorBlockString() {
158 std::string data(kBlockSize, 0);
159 memset(data.data(), 0xff, 100);
160 return data;
161 }
162
163 std::unique_ptr<TemporaryFile> base_;
164 std::unique_ptr<TemporaryFile> cow_;
165 std::vector<std::string> base_blocks_;
166 };
167
TEST_F(OfflineSnapshotTest,CompressedSnapshot)168 TEST_F(OfflineSnapshotTest, CompressedSnapshot) {
169 CowOptions options;
170 options.compression = "gz";
171 options.max_blocks = {kBlockCount};
172 options.scratch_space = false;
173
174 unique_fd cow_fd(dup(cow_->fd));
175 ASSERT_GE(cow_fd, 0);
176
177 auto writer = std::make_unique<CompressedSnapshotWriter>(options);
178 writer->SetSourceDevice(base_->path);
179 ASSERT_TRUE(writer->SetCowDevice(std::move(cow_fd)));
180 ASSERT_TRUE(writer->Initialize());
181 ASSERT_NO_FATAL_FAILURE(WriteCow(writer.get()));
182 ASSERT_NO_FATAL_FAILURE(TestReads(writer.get()));
183 }
184
185 } // namespace snapshot
186 } // namespace android
187