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 "base/unix_file/fd_file.h"
18 #include "base/unix_file/random_access_file_test.h"
19 #include "common_runtime_test.h" // For ScratchFile
20 #include "gtest/gtest.h"
21
22 namespace unix_file {
23
24 class FdFileTest : public RandomAccessFileTest {
25 protected:
MakeTestFile()26 virtual RandomAccessFile* MakeTestFile() {
27 return new FdFile(fileno(tmpfile()), false);
28 }
29 };
30
TEST_F(FdFileTest,Read)31 TEST_F(FdFileTest, Read) {
32 TestRead();
33 }
34
TEST_F(FdFileTest,SetLength)35 TEST_F(FdFileTest, SetLength) {
36 TestSetLength();
37 }
38
TEST_F(FdFileTest,Write)39 TEST_F(FdFileTest, Write) {
40 TestWrite();
41 }
42
TEST_F(FdFileTest,UnopenedFile)43 TEST_F(FdFileTest, UnopenedFile) {
44 FdFile file;
45 EXPECT_EQ(-1, file.Fd());
46 EXPECT_FALSE(file.IsOpened());
47 EXPECT_TRUE(file.GetPath().empty());
48 }
49
TEST_F(FdFileTest,OpenClose)50 TEST_F(FdFileTest, OpenClose) {
51 std::string good_path(GetTmpPath("some-file.txt"));
52 FdFile file(good_path, O_CREAT | O_WRONLY, true);
53 ASSERT_TRUE(file.IsOpened());
54 EXPECT_GE(file.Fd(), 0);
55 EXPECT_TRUE(file.IsOpened());
56 EXPECT_FALSE(file.ReadOnlyMode());
57 EXPECT_EQ(0, file.Flush());
58 EXPECT_EQ(0, file.Close());
59 EXPECT_EQ(-1, file.Fd());
60 EXPECT_FALSE(file.IsOpened());
61 FdFile file2(good_path, O_RDONLY, true);
62 EXPECT_TRUE(file2.IsOpened());
63 EXPECT_TRUE(file2.ReadOnlyMode());
64 EXPECT_GE(file2.Fd(), 0);
65
66 ASSERT_EQ(file2.Close(), 0);
67 ASSERT_EQ(unlink(good_path.c_str()), 0);
68 }
69
TEST_F(FdFileTest,ReadFullyEmptyFile)70 TEST_F(FdFileTest, ReadFullyEmptyFile) {
71 // New scratch file, zero-length.
72 art::ScratchFile tmp;
73 FdFile file(tmp.GetFilename(), O_RDONLY, false);
74 ASSERT_TRUE(file.IsOpened());
75 EXPECT_TRUE(file.ReadOnlyMode());
76 EXPECT_GE(file.Fd(), 0);
77 uint8_t buffer[16];
78 EXPECT_FALSE(file.ReadFully(&buffer, 4));
79 }
80
81 template <size_t Size>
NullTerminateCharArray(char (& array)[Size])82 static void NullTerminateCharArray(char (&array)[Size]) {
83 array[Size - 1] = '\0';
84 }
85
TEST_F(FdFileTest,ReadFullyWithOffset)86 TEST_F(FdFileTest, ReadFullyWithOffset) {
87 // New scratch file, zero-length.
88 art::ScratchFile tmp;
89 FdFile file(tmp.GetFilename(), O_RDWR, false);
90 ASSERT_TRUE(file.IsOpened());
91 EXPECT_GE(file.Fd(), 0);
92 EXPECT_FALSE(file.ReadOnlyMode());
93
94 char ignore_prefix[20] = {'a', };
95 NullTerminateCharArray(ignore_prefix);
96 char read_suffix[10] = {'b', };
97 NullTerminateCharArray(read_suffix);
98
99 off_t offset = 0;
100 // Write scratch data to file that we can read back into.
101 EXPECT_TRUE(file.Write(ignore_prefix, sizeof(ignore_prefix), offset));
102 offset += sizeof(ignore_prefix);
103 EXPECT_TRUE(file.Write(read_suffix, sizeof(read_suffix), offset));
104
105 ASSERT_EQ(file.Flush(), 0);
106
107 // Reading at an offset should only produce 'bbbb...', since we ignore the 'aaa...' prefix.
108 char buffer[sizeof(read_suffix)];
109 EXPECT_TRUE(file.PreadFully(buffer, sizeof(read_suffix), offset));
110 EXPECT_STREQ(&read_suffix[0], &buffer[0]);
111
112 ASSERT_EQ(file.Close(), 0);
113 }
114
TEST_F(FdFileTest,ReadWriteFullyWithOffset)115 TEST_F(FdFileTest, ReadWriteFullyWithOffset) {
116 // New scratch file, zero-length.
117 art::ScratchFile tmp;
118 FdFile file(tmp.GetFilename(), O_RDWR, false);
119 ASSERT_GE(file.Fd(), 0);
120 EXPECT_TRUE(file.IsOpened());
121 EXPECT_FALSE(file.ReadOnlyMode());
122
123 const char* test_string = "This is a test string";
124 size_t length = strlen(test_string) + 1;
125 const size_t offset = 12;
126 std::unique_ptr<char[]> offset_read_string(new char[length]);
127 std::unique_ptr<char[]> read_string(new char[length]);
128
129 // Write scratch data to file that we can read back into.
130 EXPECT_TRUE(file.PwriteFully(test_string, length, offset));
131 ASSERT_EQ(file.Flush(), 0);
132
133 // Test reading both the offsets.
134 EXPECT_TRUE(file.PreadFully(&offset_read_string[0], length, offset));
135 EXPECT_STREQ(test_string, &offset_read_string[0]);
136
137 EXPECT_TRUE(file.PreadFully(&read_string[0], length, 0u));
138 EXPECT_NE(memcmp(&read_string[0], test_string, length), 0);
139
140 ASSERT_EQ(file.Close(), 0);
141 }
142
TEST_F(FdFileTest,Copy)143 TEST_F(FdFileTest, Copy) {
144 art::ScratchFile src_tmp;
145 FdFile src(src_tmp.GetFilename(), O_RDWR, false);
146 ASSERT_GE(src.Fd(), 0);
147 ASSERT_TRUE(src.IsOpened());
148
149 char src_data[] = "Some test data.";
150 ASSERT_TRUE(src.WriteFully(src_data, sizeof(src_data))); // Including the zero terminator.
151 ASSERT_EQ(0, src.Flush());
152 ASSERT_EQ(static_cast<int64_t>(sizeof(src_data)), src.GetLength());
153
154 art::ScratchFile dest_tmp;
155 FdFile dest(src_tmp.GetFilename(), O_RDWR, false);
156 ASSERT_GE(dest.Fd(), 0);
157 ASSERT_TRUE(dest.IsOpened());
158
159 ASSERT_TRUE(dest.Copy(&src, 0, sizeof(src_data)));
160 ASSERT_EQ(0, dest.Flush());
161 ASSERT_EQ(static_cast<int64_t>(sizeof(src_data)), dest.GetLength());
162
163 char check_data[sizeof(src_data)];
164 ASSERT_TRUE(dest.PreadFully(check_data, sizeof(src_data), 0u));
165 CHECK_EQ(0, memcmp(check_data, src_data, sizeof(src_data)));
166
167 ASSERT_EQ(0, dest.Close());
168 ASSERT_EQ(0, src.Close());
169 }
170
TEST_F(FdFileTest,MoveConstructor)171 TEST_F(FdFileTest, MoveConstructor) {
172 // New scratch file, zero-length.
173 art::ScratchFile tmp;
174 FdFile file(tmp.GetFilename(), O_RDWR, false);
175 ASSERT_TRUE(file.IsOpened());
176 EXPECT_GE(file.Fd(), 0);
177
178 int old_fd = file.Fd();
179
180 FdFile file2(std::move(file));
181 EXPECT_FALSE(file.IsOpened());
182 EXPECT_TRUE(file2.IsOpened());
183 EXPECT_EQ(old_fd, file2.Fd());
184
185 ASSERT_EQ(file2.Flush(), 0);
186 ASSERT_EQ(file2.Close(), 0);
187 }
188
TEST_F(FdFileTest,OperatorMoveEquals)189 TEST_F(FdFileTest, OperatorMoveEquals) {
190 // Make sure the read_only_ flag is correctly copied
191 // over.
192 art::ScratchFile tmp;
193 FdFile file(tmp.GetFilename(), O_RDONLY, false);
194 ASSERT_TRUE(file.ReadOnlyMode());
195
196 FdFile file2(tmp.GetFilename(), O_RDWR, false);
197 ASSERT_FALSE(file2.ReadOnlyMode());
198
199 file2 = std::move(file);
200 ASSERT_TRUE(file2.ReadOnlyMode());
201 }
202
TEST_F(FdFileTest,EraseWithPathUnlinks)203 TEST_F(FdFileTest, EraseWithPathUnlinks) {
204 // New scratch file, zero-length.
205 art::ScratchFile tmp;
206 std::string filename = tmp.GetFilename();
207 tmp.Close(); // This is required because of the unlink race between the scratch file and the
208 // FdFile, which leads to close-guard breakage.
209 FdFile file(filename, O_RDWR, false);
210 ASSERT_TRUE(file.IsOpened());
211 EXPECT_GE(file.Fd(), 0);
212 uint8_t buffer[16] = { 0 };
213 EXPECT_TRUE(file.WriteFully(&buffer, sizeof(buffer)));
214 EXPECT_EQ(file.Flush(), 0);
215
216 EXPECT_TRUE(file.Erase(true));
217
218 EXPECT_FALSE(file.IsOpened());
219
220 EXPECT_FALSE(art::OS::FileExists(filename.c_str())) << filename;
221 }
222
TEST_F(FdFileTest,Compare)223 TEST_F(FdFileTest, Compare) {
224 std::vector<uint8_t> buffer;
225 constexpr int64_t length = 17 * art::KB;
226 for (size_t i = 0; i < length; ++i) {
227 buffer.push_back(static_cast<uint8_t>(i));
228 }
229
230 auto reset_compare = [&](art::ScratchFile& a, art::ScratchFile& b) {
231 a.GetFile()->ResetOffset();
232 b.GetFile()->ResetOffset();
233 return a.GetFile()->Compare(b.GetFile());
234 };
235
236 art::ScratchFile tmp;
237 EXPECT_TRUE(tmp.GetFile()->WriteFully(&buffer[0], length));
238 EXPECT_EQ(tmp.GetFile()->GetLength(), length);
239
240 art::ScratchFile tmp2;
241 EXPECT_TRUE(tmp2.GetFile()->WriteFully(&buffer[0], length));
242 EXPECT_EQ(tmp2.GetFile()->GetLength(), length);
243
244 // Basic equality check.
245 tmp.GetFile()->ResetOffset();
246 tmp2.GetFile()->ResetOffset();
247 // Files should be the same.
248 EXPECT_EQ(reset_compare(tmp, tmp2), 0);
249
250 // Change a byte near the start.
251 ++buffer[2];
252 art::ScratchFile tmp3;
253 EXPECT_TRUE(tmp3.GetFile()->WriteFully(&buffer[0], length));
254 --buffer[2];
255 EXPECT_NE(reset_compare(tmp, tmp3), 0);
256
257 // Change a byte near the middle.
258 ++buffer[length / 2];
259 art::ScratchFile tmp4;
260 EXPECT_TRUE(tmp4.GetFile()->WriteFully(&buffer[0], length));
261 --buffer[length / 2];
262 EXPECT_NE(reset_compare(tmp, tmp4), 0);
263
264 // Change a byte near the end.
265 ++buffer[length - 5];
266 art::ScratchFile tmp5;
267 EXPECT_TRUE(tmp5.GetFile()->WriteFully(&buffer[0], length));
268 --buffer[length - 5];
269 EXPECT_NE(reset_compare(tmp, tmp5), 0);
270
271 // Reference check
272 art::ScratchFile tmp6;
273 EXPECT_TRUE(tmp6.GetFile()->WriteFully(&buffer[0], length));
274 EXPECT_EQ(reset_compare(tmp, tmp6), 0);
275 }
276
TEST_F(FdFileTest,PipeFlush)277 TEST_F(FdFileTest, PipeFlush) {
278 int pipefd[2];
279 ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
280
281 FdFile file(pipefd[1], true);
282 ASSERT_TRUE(file.WriteFully("foo", 3));
283 ASSERT_EQ(0, file.Flush());
284 ASSERT_EQ(0, file.FlushCloseOrErase());
285 close(pipefd[0]);
286 }
287
288 } // namespace unix_file
289