1 //
2 // Copyright (C) 2009 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/extent_writer.h"
18
19 #include <fcntl.h>
20
21 #include <algorithm>
22 #include <memory>
23 #include <string>
24 #include <vector>
25
26 #include <brillo/secure_blob.h>
27 #include <gtest/gtest.h>
28
29 #include "update_engine/common/test_utils.h"
30 #include "update_engine/common/utils.h"
31 #include "update_engine/payload_consumer/payload_constants.h"
32 #include "update_engine/payload_generator/extent_ranges.h"
33
34 using chromeos_update_engine::test_utils::ExpectVectorsEq;
35 using std::min;
36 using std::string;
37 using std::vector;
38
39 namespace chromeos_update_engine {
40
41 static_assert(sizeof(off_t) == 8, "off_t not 64 bit");
42
43 namespace {
44 const size_t kBlockSize = 4096;
45 }
46
47 class ExtentWriterTest : public ::testing::Test {
48 protected:
SetUp()49 void SetUp() override {
50 fd_.reset(new EintrSafeFileDescriptor);
51 ASSERT_TRUE(fd_->Open(temp_file_.path().c_str(), O_RDWR, 0600));
52 }
TearDown()53 void TearDown() override {
54 fd_->Close();
55 }
56
57 // Writes data to an extent writer in 'chunk_size' chunks with
58 // the first chunk of size first_chunk_size. It calculates what the
59 // resultant file should look like and ensure that the extent writer
60 // wrote the file correctly.
61 void WriteAlignedExtents(size_t chunk_size, size_t first_chunk_size);
62 void TestZeroPad(bool aligned_size);
63
64 FileDescriptorPtr fd_;
65 test_utils::ScopedTempFile temp_file_{"ExtentWriterTest-file.XXXXXX"};
66 };
67
TEST_F(ExtentWriterTest,SimpleTest)68 TEST_F(ExtentWriterTest, SimpleTest) {
69 vector<Extent> extents = {ExtentForRange(1, 1)};
70 const string bytes = "1234";
71 DirectExtentWriter direct_writer;
72 EXPECT_TRUE(
73 direct_writer.Init(fd_, {extents.begin(), extents.end()}, kBlockSize));
74 EXPECT_TRUE(direct_writer.Write(bytes.data(), bytes.size()));
75 EXPECT_TRUE(direct_writer.End());
76
77 EXPECT_EQ(static_cast<off_t>(kBlockSize + bytes.size()),
78 utils::FileSize(temp_file_.path()));
79
80 brillo::Blob result_file;
81 EXPECT_TRUE(utils::ReadFile(temp_file_.path(), &result_file));
82
83 brillo::Blob expected_file(kBlockSize);
84 expected_file.insert(expected_file.end(),
85 bytes.data(), bytes.data() + bytes.size());
86 ExpectVectorsEq(expected_file, result_file);
87 }
88
TEST_F(ExtentWriterTest,ZeroLengthTest)89 TEST_F(ExtentWriterTest, ZeroLengthTest) {
90 vector<Extent> extents = {ExtentForRange(1, 1)};
91 DirectExtentWriter direct_writer;
92 EXPECT_TRUE(
93 direct_writer.Init(fd_, {extents.begin(), extents.end()}, kBlockSize));
94 EXPECT_TRUE(direct_writer.Write(nullptr, 0));
95 EXPECT_TRUE(direct_writer.End());
96 }
97
TEST_F(ExtentWriterTest,OverflowExtentTest)98 TEST_F(ExtentWriterTest, OverflowExtentTest) {
99 WriteAlignedExtents(kBlockSize * 3, kBlockSize * 3);
100 }
101
TEST_F(ExtentWriterTest,UnalignedWriteTest)102 TEST_F(ExtentWriterTest, UnalignedWriteTest) {
103 WriteAlignedExtents(7, 7);
104 }
105
TEST_F(ExtentWriterTest,LargeUnalignedWriteTest)106 TEST_F(ExtentWriterTest, LargeUnalignedWriteTest) {
107 WriteAlignedExtents(kBlockSize * 2, kBlockSize / 2);
108 }
109
WriteAlignedExtents(size_t chunk_size,size_t first_chunk_size)110 void ExtentWriterTest::WriteAlignedExtents(size_t chunk_size,
111 size_t first_chunk_size) {
112 vector<Extent> extents = {
113 ExtentForRange(1, 1), ExtentForRange(0, 1), ExtentForRange(2, 1)};
114 brillo::Blob data(kBlockSize * 3);
115 test_utils::FillWithData(&data);
116
117 DirectExtentWriter direct_writer;
118 EXPECT_TRUE(
119 direct_writer.Init(fd_, {extents.begin(), extents.end()}, kBlockSize));
120
121 size_t bytes_written = 0;
122 while (bytes_written < data.size()) {
123 size_t bytes_to_write = min(data.size() - bytes_written, chunk_size);
124 if (bytes_written == 0) {
125 bytes_to_write = min(data.size() - bytes_written, first_chunk_size);
126 }
127 EXPECT_TRUE(direct_writer.Write(&data[bytes_written], bytes_to_write));
128 bytes_written += bytes_to_write;
129 }
130 EXPECT_TRUE(direct_writer.End());
131
132 EXPECT_EQ(static_cast<off_t>(data.size()),
133 utils::FileSize(temp_file_.path()));
134
135 brillo::Blob result_file;
136 EXPECT_TRUE(utils::ReadFile(temp_file_.path(), &result_file));
137
138 brillo::Blob expected_file;
139 expected_file.insert(expected_file.end(),
140 data.begin() + kBlockSize,
141 data.begin() + kBlockSize * 2);
142 expected_file.insert(expected_file.end(),
143 data.begin(), data.begin() + kBlockSize);
144 expected_file.insert(expected_file.end(),
145 data.begin() + kBlockSize * 2, data.end());
146 ExpectVectorsEq(expected_file, result_file);
147 }
148
TEST_F(ExtentWriterTest,ZeroPadNullTest)149 TEST_F(ExtentWriterTest, ZeroPadNullTest) {
150 TestZeroPad(true);
151 }
152
TEST_F(ExtentWriterTest,ZeroPadFillTest)153 TEST_F(ExtentWriterTest, ZeroPadFillTest) {
154 TestZeroPad(false);
155 }
156
TestZeroPad(bool aligned_size)157 void ExtentWriterTest::TestZeroPad(bool aligned_size) {
158 vector<Extent> extents = {ExtentForRange(1, 1), ExtentForRange(0, 1)};
159 brillo::Blob data(kBlockSize * 2);
160 test_utils::FillWithData(&data);
161
162 ZeroPadExtentWriter zero_pad_writer(std::make_unique<DirectExtentWriter>());
163
164 EXPECT_TRUE(
165 zero_pad_writer.Init(fd_, {extents.begin(), extents.end()}, kBlockSize));
166 size_t bytes_to_write = data.size();
167 const size_t missing_bytes = (aligned_size ? 0 : 9);
168 bytes_to_write -= missing_bytes;
169 fd_->Seek(kBlockSize - missing_bytes, SEEK_SET);
170 EXPECT_EQ(3, fd_->Write("xxx", 3));
171 ASSERT_TRUE(zero_pad_writer.Write(data.data(), bytes_to_write));
172 EXPECT_TRUE(zero_pad_writer.End());
173
174 EXPECT_EQ(static_cast<off_t>(data.size()),
175 utils::FileSize(temp_file_.path()));
176
177 brillo::Blob result_file;
178 EXPECT_TRUE(utils::ReadFile(temp_file_.path(), &result_file));
179
180 brillo::Blob expected_file;
181 expected_file.insert(expected_file.end(),
182 data.begin() + kBlockSize,
183 data.begin() + kBlockSize * 2);
184 expected_file.insert(expected_file.end(),
185 data.begin(), data.begin() + kBlockSize);
186 if (missing_bytes) {
187 memset(&expected_file[kBlockSize - missing_bytes], 0, missing_bytes);
188 }
189
190 ExpectVectorsEq(expected_file, result_file);
191 }
192
TEST_F(ExtentWriterTest,SparseFileTest)193 TEST_F(ExtentWriterTest, SparseFileTest) {
194 vector<Extent> extents = {ExtentForRange(1, 1),
195 ExtentForRange(kSparseHole, 2),
196 ExtentForRange(0, 1)};
197 const int block_count = 4;
198 const int on_disk_count = 2;
199
200 brillo::Blob data(17);
201 test_utils::FillWithData(&data);
202
203 DirectExtentWriter direct_writer;
204 EXPECT_TRUE(
205 direct_writer.Init(fd_, {extents.begin(), extents.end()}, kBlockSize));
206
207 size_t bytes_written = 0;
208 while (bytes_written < (block_count * kBlockSize)) {
209 size_t bytes_to_write = min(block_count * kBlockSize - bytes_written,
210 data.size());
211 EXPECT_TRUE(direct_writer.Write(data.data(), bytes_to_write));
212 bytes_written += bytes_to_write;
213 }
214 EXPECT_TRUE(direct_writer.End());
215
216 // check file size, then data inside
217 ASSERT_EQ(static_cast<off_t>(2 * kBlockSize),
218 utils::FileSize(temp_file_.path()));
219
220 brillo::Blob resultant_data;
221 EXPECT_TRUE(utils::ReadFile(temp_file_.path(), &resultant_data));
222
223 // Create expected data
224 brillo::Blob expected_data(on_disk_count * kBlockSize);
225 brillo::Blob big(block_count * kBlockSize);
226 for (brillo::Blob::size_type i = 0; i < big.size(); i++) {
227 big[i] = data[i % data.size()];
228 }
229 memcpy(&expected_data[kBlockSize], &big[0], kBlockSize);
230 memcpy(&expected_data[0], &big[3 * kBlockSize], kBlockSize);
231 ExpectVectorsEq(expected_data, resultant_data);
232 }
233
234 } // namespace chromeos_update_engine
235