1 // Copyright 2018 Google Inc.
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 ///////////////////////////////////////////////////////////////////////////////
16 #include "tink/util/file_input_stream.h"
17
18 #include <fcntl.h>
19
20 #include <algorithm>
21 #include <cstdint>
22 #include <cstring>
23 #include <iostream>
24 #include <ostream>
25 #include <string>
26
27 #include "gmock/gmock.h"
28 #include "gtest/gtest.h"
29 #include "absl/memory/memory.h"
30 #include "absl/status/status.h"
31 #include "absl/strings/str_cat.h"
32 #include "absl/strings/string_view.h"
33 #include "tink/internal/test_file_util.h"
34 #include "tink/subtle/random.h"
35 #include "tink/util/status.h"
36 #include "tink/util/test_matchers.h"
37 #include "tink/util/test_util.h"
38
39 namespace crypto {
40 namespace tink {
41 namespace {
42
43 using ::crypto::tink::test::IsOk;
44 using ::crypto::tink::test::IsOkAndHolds;
45 using ::crypto::tink::test::StatusIs;
46
47 constexpr int kDefaultTestStreamSize = 100 * 1024; // 100 KB.
48
49 // Opens test file `filename` and returns a file descriptor to it.
OpenTestFileToRead(absl::string_view filename)50 util::StatusOr<int> OpenTestFileToRead(absl::string_view filename) {
51 std::string full_filename = absl::StrCat(test::TmpDir(), "/", filename);
52 int fd = open(full_filename.c_str(), O_RDONLY);
53 if (fd == -1) {
54 return util::Status(absl::StatusCode::kInternal,
55 absl::StrCat("Cannot open file ", full_filename,
56 " error: ", std::strerror(errno)));
57 }
58 return fd;
59 }
60
61 // Reads the specified `input_stream` until no more bytes can be read,
62 // and puts the read bytes into `contents`.
63 // Returns the status of the last input_stream->Next()-operation.
ReadAll(util::FileInputStream * input_stream,std::string * contents)64 util::Status ReadAll(util::FileInputStream* input_stream,
65 std::string* contents) {
66 contents->clear();
67 const void* buffer;
68 auto next_result = input_stream->Next(&buffer);
69 while (next_result.ok()) {
70 contents->append(static_cast<const char*>(buffer), next_result.value());
71 next_result = input_stream->Next(&buffer);
72 }
73 return next_result.status();
74 }
75
76 using FileInputStreamTestDefaultBufferSize = testing::TestWithParam<int>;
77
TEST_P(FileInputStreamTestDefaultBufferSize,ReadAllfFromInputStreamSucceeds)78 TEST_P(FileInputStreamTestDefaultBufferSize, ReadAllfFromInputStreamSucceeds) {
79 int stream_size = GetParam();
80 SCOPED_TRACE(absl::StrCat("stream_size = ", stream_size));
81 std::string file_contents = subtle::Random::GetRandomBytes(stream_size);
82 std::string filename = absl::StrCat(
83 stream_size, "_", internal::GetTestFileNamePrefix(), "_file.bin");
84 ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk());
85 util::StatusOr<int> input_fd = OpenTestFileToRead(filename);
86 ASSERT_THAT(input_fd.status(), IsOk());
87 EXPECT_EQ(stream_size, file_contents.size());
88 auto input_stream = absl::make_unique<util::FileInputStream>(*input_fd);
89 std::string stream_contents;
90 auto status = ReadAll(input_stream.get(), &stream_contents);
91 EXPECT_THAT(status, StatusIs(absl::StatusCode::kOutOfRange));
92 EXPECT_EQ(status.message(), "EOF");
93 EXPECT_EQ(file_contents, stream_contents);
94 }
95
96 INSTANTIATE_TEST_SUITE_P(FileInputStreamTest,
97 FileInputStreamTestDefaultBufferSize,
98 testing::ValuesIn({0, 10, 100, 1000, 10000, 100000,
99 1000000}));
100
101 using FileInputStreamTestCustomBufferSizes = testing::TestWithParam<int>;
102
TEST_P(FileInputStreamTestCustomBufferSizes,ReadAllWithCustomBufferSizeSucceeds)103 TEST_P(FileInputStreamTestCustomBufferSizes,
104 ReadAllWithCustomBufferSizeSucceeds) {
105 int buffer_size = GetParam();
106 SCOPED_TRACE(absl::StrCat("buffer_size = ", buffer_size));
107 std::string file_contents =
108 subtle::Random::GetRandomBytes(kDefaultTestStreamSize);
109 std::string filename = absl::StrCat(
110 buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin");
111 ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk());
112 util::StatusOr<int> input_fd = OpenTestFileToRead(filename);
113 ASSERT_THAT(input_fd.status(), IsOk());
114 EXPECT_EQ(kDefaultTestStreamSize, file_contents.size());
115 auto input_stream =
116 absl::make_unique<util::FileInputStream>(*input_fd, buffer_size);
117 const void* buffer;
118 auto next_result = input_stream->Next(&buffer);
119 ASSERT_THAT(next_result, IsOk());
120 EXPECT_EQ(buffer_size, next_result.value());
121 EXPECT_EQ(file_contents.substr(0, buffer_size),
122 std::string(static_cast<const char*>(buffer), buffer_size));
123 }
124
125 INSTANTIATE_TEST_SUITE_P(FileInputStreamTest,
126 FileInputStreamTestCustomBufferSizes,
127 testing::ValuesIn({1, 10, 100, 1000, 10000}));
128
TEST(FileInputStreamTest,NextFailsIfFdIsInvalid)129 TEST(FileInputStreamTest, NextFailsIfFdIsInvalid) {
130 int buffer_size = 4 * 1024;
131 auto input_stream = absl::make_unique<util::FileInputStream>(-1, buffer_size);
132 const void* buffer = nullptr;
133 EXPECT_THAT(input_stream->Next(&buffer).status(),
134 StatusIs(absl::StatusCode::kInternal));
135 }
136
TEST(FileInputStreamTest,NextFailsIfDataIsNull)137 TEST(FileInputStreamTest, NextFailsIfDataIsNull) {
138 int buffer_size = 4 * 1024;
139 std::string file_contents =
140 subtle::Random::GetRandomBytes(kDefaultTestStreamSize);
141 std::string filename = absl::StrCat(
142 buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin");
143 ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk());
144 util::StatusOr<int> input_fd = OpenTestFileToRead(filename);
145 ASSERT_THAT(input_fd.status(), IsOk());
146 EXPECT_EQ(kDefaultTestStreamSize, file_contents.size());
147 auto input_stream =
148 absl::make_unique<util::FileInputStream>(*input_fd, buffer_size);
149
150 EXPECT_THAT(input_stream->Next(nullptr).status(),
151 StatusIs(absl::StatusCode::kInvalidArgument));
152 }
153
TEST(FileInputStreamTest,NextReadsExactlyOneBlockOfData)154 TEST(FileInputStreamTest, NextReadsExactlyOneBlockOfData) {
155 int buffer_size = 4 * 1024;
156 std::string file_contents =
157 subtle::Random::GetRandomBytes(kDefaultTestStreamSize);
158 std::string filename = absl::StrCat(
159 buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin");
160 ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk());
161 util::StatusOr<int> input_fd = OpenTestFileToRead(filename);
162 ASSERT_THAT(input_fd.status(), IsOk());
163 EXPECT_EQ(kDefaultTestStreamSize, file_contents.size());
164 auto input_stream =
165 absl::make_unique<util::FileInputStream>(*input_fd, buffer_size);
166
167 auto expected_file_content_block =
168 absl::string_view(file_contents).substr(0, buffer_size);
169 const void* buffer = nullptr;
170 util::StatusOr<int> next_result = input_stream->Next(&buffer);
171 ASSERT_THAT(next_result, IsOkAndHolds(buffer_size));
172 // Check that we advanced of buffer_size bytes.
173 EXPECT_EQ(input_stream->Position(), buffer_size);
174 EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size),
175 expected_file_content_block);
176 }
177
TEST(FileInputStreamTest,BackupForNegativeOrZeroBytesIsANoop)178 TEST(FileInputStreamTest, BackupForNegativeOrZeroBytesIsANoop) {
179 int buffer_size = 4 * 1024;
180 std::string file_contents =
181 subtle::Random::GetRandomBytes(kDefaultTestStreamSize);
182 std::string filename = absl::StrCat(
183 buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin");
184 ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk());
185 util::StatusOr<int> input_fd = OpenTestFileToRead(filename);
186 ASSERT_THAT(input_fd.status(), IsOk());
187 EXPECT_EQ(kDefaultTestStreamSize, file_contents.size());
188 auto input_stream =
189 absl::make_unique<util::FileInputStream>(*input_fd, buffer_size);
190 EXPECT_EQ(input_stream->Position(), 0);
191
192 auto expected_file_content_block =
193 absl::string_view(file_contents).substr(0, buffer_size);
194 const void* buffer = nullptr;
195 ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size));
196 // Check that we advanced of buffer_size bytes.
197 EXPECT_EQ(input_stream->Position(), buffer_size);
198 EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size),
199 expected_file_content_block);
200
201 // The calls below are noops.
202 input_stream->BackUp(0);
203 EXPECT_EQ(input_stream->Position(), buffer_size);
204 input_stream->BackUp(-12);
205 EXPECT_EQ(input_stream->Position(), buffer_size);
206
207 // A subsequent call to `Next` returns the 2nd block.
208 auto expected_2nd_file_content_block =
209 absl::string_view(file_contents).substr(buffer_size, buffer_size);
210 ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size));
211 // Check that we advanced of buffer_size bytes.
212 EXPECT_EQ(input_stream->Position(), 2 * buffer_size);
213 EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size),
214 expected_2nd_file_content_block);
215 }
216
TEST(FileInputStreamTest,BackupForLessThanOneBlockOfData)217 TEST(FileInputStreamTest, BackupForLessThanOneBlockOfData) {
218 int buffer_size = 4 * 1024;
219 std::string file_contents =
220 subtle::Random::GetRandomBytes(kDefaultTestStreamSize);
221 std::string filename = absl::StrCat(
222 buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin");
223 ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk());
224 util::StatusOr<int> input_fd = OpenTestFileToRead(filename);
225 ASSERT_THAT(input_fd.status(), IsOk());
226 EXPECT_EQ(kDefaultTestStreamSize, file_contents.size());
227 auto input_stream =
228 absl::make_unique<util::FileInputStream>(*input_fd, buffer_size);
229
230 auto expected_file_content_block =
231 absl::string_view(file_contents).substr(0, buffer_size);
232 const void* buffer = nullptr;
233 ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size));
234 // Check that we advanced of buffer_size bytes.
235 EXPECT_EQ(input_stream->Position(), buffer_size);
236 EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size),
237 expected_file_content_block);
238
239 int64_t position_after_next = input_stream->Position();
240 // Number of bytes that were backed up.
241 int num_backed_up_bytes = 0;
242 input_stream->BackUp(0); // This should be a noop.
243 EXPECT_EQ(input_stream->Position(), position_after_next);
244 input_stream->BackUp(-12); // This should be a noop.
245 EXPECT_EQ(input_stream->Position(), position_after_next);
246 input_stream->BackUp(10);
247 num_backed_up_bytes += 10;
248 EXPECT_EQ(input_stream->Position(),
249 position_after_next - num_backed_up_bytes);
250 input_stream->BackUp(5);
251 num_backed_up_bytes += 5;
252 EXPECT_EQ(input_stream->Position(),
253 position_after_next - num_backed_up_bytes);
254
255 // A subsequent call to Next should return only the backed up bytes.
256 auto expected_backed_up_bytes =
257 absl::string_view(file_contents)
258 .substr(buffer_size - num_backed_up_bytes, num_backed_up_bytes);
259 ASSERT_THAT(input_stream->Next(&buffer),
260 IsOkAndHolds(expected_backed_up_bytes.size()));
261 EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer),
262 expected_backed_up_bytes.size()),
263 expected_backed_up_bytes);
264 }
265
266 // When backing up of a number of bytes larger than the size of a block, backup
267 // of one block.
TEST(FileInputStreamTest,BackupAtMostOfOneBlock)268 TEST(FileInputStreamTest, BackupAtMostOfOneBlock) {
269 int buffer_size = 4 * 1024;
270 std::string file_contents =
271 subtle::Random::GetRandomBytes(kDefaultTestStreamSize);
272 std::string filename = absl::StrCat(
273 buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin");
274 ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk());
275 util::StatusOr<int> input_fd = OpenTestFileToRead(filename);
276 ASSERT_THAT(input_fd.status(), IsOk());
277 EXPECT_EQ(kDefaultTestStreamSize, file_contents.size());
278 auto input_stream =
279 absl::make_unique<util::FileInputStream>(*input_fd, buffer_size);
280
281 // Read two blocks of size buffer_size, then back up of more than buffer_size
282 // bytes.
283 auto expected_1st_file_content_block =
284 absl::string_view(file_contents).substr(0, buffer_size);
285 const void* buffer = nullptr;
286 ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size));
287 // Check that we advanced of buffer_size bytes.
288 EXPECT_EQ(input_stream->Position(), buffer_size);
289 EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size),
290 expected_1st_file_content_block);
291
292 auto expected_2nd_file_content_block =
293 absl::string_view(file_contents).substr(buffer_size, buffer_size);
294 ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size));
295 // Check that we advanced of buffer_size bytes.
296 EXPECT_EQ(input_stream->Position(), 2 * buffer_size);
297 EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size),
298 expected_2nd_file_content_block);
299
300 int64_t position_after_next = input_stream->Position();
301 EXPECT_EQ(input_stream->Position(), position_after_next);
302 input_stream->BackUp(10);
303 EXPECT_EQ(input_stream->Position(), position_after_next - 10);
304 input_stream->BackUp(buffer_size);
305 EXPECT_EQ(input_stream->Position(), position_after_next - buffer_size);
306
307 // This call to Next is expected to read the second block again.
308 ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size));
309 EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size),
310 expected_2nd_file_content_block);
311 }
312
313 } // namespace
314 } // namespace tink
315 } // namespace crypto
316