1 /*
2 * Copyright (C) 2008 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/mapped_file.h"
18 #include "base/casts.h"
19 #include "base/logging.h"
20 #include "base/unix_file/fd_file.h"
21 #include "base/unix_file/random_access_file_test.h"
22 #include "base/unix_file/random_access_file_utils.h"
23 #include "base/unix_file/string_file.h"
24 #include "gtest/gtest.h"
25
26 namespace unix_file {
27
28 class MappedFileTest : public RandomAccessFileTest {
29 protected:
MappedFileTest()30 MappedFileTest() : kContent("some content") {
31 }
32
SetUp()33 void SetUp() {
34 RandomAccessFileTest::SetUp();
35
36 good_path_ = GetTmpPath("some-file.txt");
37 int fd = TEMP_FAILURE_RETRY(open(good_path_.c_str(), O_CREAT|O_RDWR, 0666));
38 FdFile dst(fd, false);
39
40 StringFile src;
41 src.Assign(kContent);
42
43 ASSERT_TRUE(CopyFile(src, &dst));
44 ASSERT_EQ(dst.FlushClose(), 0);
45 }
46
TearDown()47 void TearDown() {
48 ASSERT_EQ(unlink(good_path_.c_str()), 0);
49
50 RandomAccessFileTest::TearDown();
51 }
52
MakeTestFile()53 virtual RandomAccessFile* MakeTestFile() {
54 TEMP_FAILURE_RETRY(truncate(good_path_.c_str(), 0));
55 MappedFile* f = new MappedFile;
56 CHECK(f->Open(good_path_, MappedFile::kReadWriteMode));
57 return f;
58 }
59
CleanUp(RandomAccessFile * file)60 void CleanUp(RandomAccessFile* file) OVERRIDE {
61 if (file == nullptr) {
62 return;
63 }
64 MappedFile* f = ::art::down_cast<MappedFile*>(file);
65 UNUSED(f->Flush());
66 UNUSED(f->Close());
67 }
68
69 const std::string kContent;
70 std::string good_path_;
71 };
72
TEST_F(MappedFileTest,OkayToNotUse)73 TEST_F(MappedFileTest, OkayToNotUse) {
74 MappedFile file;
75 EXPECT_EQ(-1, file.Fd());
76 EXPECT_FALSE(file.IsOpened());
77 EXPECT_FALSE(file.IsMapped());
78 }
79
TEST_F(MappedFileTest,OpenClose)80 TEST_F(MappedFileTest, OpenClose) {
81 MappedFile file;
82 ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
83 EXPECT_GE(file.Fd(), 0);
84 EXPECT_TRUE(file.IsOpened());
85 EXPECT_EQ(kContent.size(), static_cast<uint64_t>(file.size()));
86 EXPECT_EQ(0, file.Close());
87 EXPECT_EQ(-1, file.Fd());
88 EXPECT_FALSE(file.IsOpened());
89 }
90
TEST_F(MappedFileTest,OpenFdClose)91 TEST_F(MappedFileTest, OpenFdClose) {
92 FILE* f = tmpfile();
93 ASSERT_TRUE(f != NULL);
94 MappedFile file(fileno(f), false);
95 EXPECT_GE(file.Fd(), 0);
96 EXPECT_TRUE(file.IsOpened());
97 EXPECT_EQ(0, file.Close());
98 }
99
TEST_F(MappedFileTest,CanUseAfterMapReadOnly)100 TEST_F(MappedFileTest, CanUseAfterMapReadOnly) {
101 MappedFile file;
102 ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
103 EXPECT_FALSE(file.IsMapped());
104 EXPECT_TRUE(file.MapReadOnly());
105 EXPECT_TRUE(file.IsMapped());
106 EXPECT_EQ(kContent.size(), static_cast<uint64_t>(file.size()));
107 ASSERT_TRUE(file.data());
108 EXPECT_EQ(0, memcmp(kContent.c_str(), file.data(), file.size()));
109 EXPECT_EQ(0, file.Flush());
110 }
111
TEST_F(MappedFileTest,CanUseAfterMapReadWrite)112 TEST_F(MappedFileTest, CanUseAfterMapReadWrite) {
113 MappedFile file;
114 ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
115 EXPECT_FALSE(file.IsMapped());
116 EXPECT_TRUE(file.MapReadWrite(1));
117 EXPECT_TRUE(file.IsMapped());
118 EXPECT_EQ(1, file.size());
119 ASSERT_TRUE(file.data());
120 EXPECT_EQ(kContent[0], *file.data());
121 EXPECT_EQ(0, file.Flush());
122 file.Close();
123 }
124
TEST_F(MappedFileTest,CanWriteNewData)125 TEST_F(MappedFileTest, CanWriteNewData) {
126 const std::string new_path(GetTmpPath("new-file.txt"));
127 ASSERT_EQ(-1, unlink(new_path.c_str()));
128 ASSERT_EQ(ENOENT, errno);
129
130 MappedFile file;
131 ASSERT_TRUE(file.Open(new_path, MappedFile::kReadWriteMode));
132 EXPECT_TRUE(file.MapReadWrite(kContent.size()));
133 EXPECT_TRUE(file.IsMapped());
134 EXPECT_EQ(kContent.size(), static_cast<uint64_t>(file.size()));
135 ASSERT_TRUE(file.data());
136 memcpy(file.data(), kContent.c_str(), kContent.size());
137 EXPECT_EQ(0, file.Flush());
138 EXPECT_EQ(0, file.Close());
139 EXPECT_FALSE(file.IsMapped());
140
141 FdFile new_file(TEMP_FAILURE_RETRY(open(new_path.c_str(), O_RDONLY)), false);
142 StringFile buffer;
143 ASSERT_TRUE(CopyFile(new_file, &buffer));
144 EXPECT_EQ(kContent, buffer.ToStringPiece());
145 EXPECT_EQ(0, unlink(new_path.c_str()));
146 }
147
TEST_F(MappedFileTest,FileMustExist)148 TEST_F(MappedFileTest, FileMustExist) {
149 const std::string bad_path(GetTmpPath("does-not-exist.txt"));
150 MappedFile file;
151 EXPECT_FALSE(file.Open(bad_path, MappedFile::kReadOnlyMode));
152 EXPECT_EQ(-1, file.Fd());
153 }
154
TEST_F(MappedFileTest,FileMustBeWritable)155 TEST_F(MappedFileTest, FileMustBeWritable) {
156 MappedFile file;
157 ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
158 EXPECT_FALSE(file.MapReadWrite(10));
159 }
160
TEST_F(MappedFileTest,RemappingAllowedUntilSuccess)161 TEST_F(MappedFileTest, RemappingAllowedUntilSuccess) {
162 MappedFile file;
163 ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
164 EXPECT_FALSE(file.MapReadWrite(10));
165 EXPECT_FALSE(file.MapReadWrite(10));
166 }
167
TEST_F(MappedFileTest,ResizeMappedFile)168 TEST_F(MappedFileTest, ResizeMappedFile) {
169 MappedFile file;
170 ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
171 ASSERT_TRUE(file.MapReadWrite(10));
172 EXPECT_EQ(10, file.GetLength());
173 EXPECT_TRUE(file.Unmap());
174 EXPECT_TRUE(file.MapReadWrite(20));
175 EXPECT_EQ(20, file.GetLength());
176 EXPECT_EQ(0, file.Flush());
177 EXPECT_TRUE(file.Unmap());
178 EXPECT_EQ(0, file.Flush());
179 EXPECT_EQ(0, file.SetLength(5));
180 EXPECT_TRUE(file.MapReadOnly());
181 EXPECT_EQ(5, file.GetLength());
182 }
183
TEST_F(MappedFileTest,ReadNotMapped)184 TEST_F(MappedFileTest, ReadNotMapped) {
185 TestRead();
186 }
187
TEST_F(MappedFileTest,SetLengthNotMapped)188 TEST_F(MappedFileTest, SetLengthNotMapped) {
189 TestSetLength();
190 }
191
TEST_F(MappedFileTest,WriteNotMapped)192 TEST_F(MappedFileTest, WriteNotMapped) {
193 TestWrite();
194 }
195
TEST_F(MappedFileTest,ReadMappedReadOnly)196 TEST_F(MappedFileTest, ReadMappedReadOnly) {
197 MappedFile file;
198 ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
199 ASSERT_TRUE(file.MapReadOnly());
200 TestReadContent(kContent, &file);
201 }
202
TEST_F(MappedFileTest,ReadMappedReadWrite)203 TEST_F(MappedFileTest, ReadMappedReadWrite) {
204 MappedFile file;
205 ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
206 ASSERT_TRUE(file.MapReadWrite(kContent.size()));
207 TestReadContent(kContent, &file);
208 UNUSED(file.FlushClose());
209 }
210
TEST_F(MappedFileTest,WriteMappedReadWrite)211 TEST_F(MappedFileTest, WriteMappedReadWrite) {
212 TEMP_FAILURE_RETRY(unlink(good_path_.c_str()));
213 MappedFile file;
214 ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
215 ASSERT_TRUE(file.MapReadWrite(kContent.size()));
216
217 // Can't write to a negative offset.
218 EXPECT_EQ(-EINVAL, file.Write(kContent.c_str(), 0, -123));
219
220 // A zero-length write is a no-op.
221 EXPECT_EQ(0, file.Write(kContent.c_str(), 0, 0));
222 // But the file size is as given when mapped.
223 EXPECT_EQ(kContent.size(), static_cast<uint64_t>(file.GetLength()));
224
225 // Data written past the end are discarded.
226 EXPECT_EQ(kContent.size() - 1,
227 static_cast<uint64_t>(file.Write(kContent.c_str(), kContent.size(), 1)));
228 EXPECT_EQ(0, memcmp(kContent.c_str(), file.data() + 1, kContent.size() - 1));
229
230 // Data can be overwritten.
231 EXPECT_EQ(kContent.size(),
232 static_cast<uint64_t>(file.Write(kContent.c_str(), kContent.size(), 0)));
233 EXPECT_EQ(0, memcmp(kContent.c_str(), file.data(), kContent.size()));
234 UNUSED(file.FlushClose());
235 }
236
237 #if 0 // death tests don't work on android yet
238
239 class MappedFileDeathTest : public MappedFileTest {};
240
241 TEST_F(MappedFileDeathTest, MustMapBeforeUse) {
242 MappedFile file;
243 EXPECT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
244 EXPECT_DEATH(file.data(), "mapped_");
245 }
246
247 TEST_F(MappedFileDeathTest, RemappingNotAllowedReadOnly) {
248 MappedFile file;
249 ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
250 ASSERT_TRUE(file.MapReadOnly());
251 EXPECT_DEATH(file.MapReadOnly(), "mapped_");
252 }
253
254 TEST_F(MappedFileDeathTest, RemappingNotAllowedReadWrite) {
255 MappedFile file;
256 ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
257 ASSERT_TRUE(file.MapReadWrite(10));
258 EXPECT_DEATH(file.MapReadWrite(10), "mapped_");
259 }
260
261 TEST_F(MappedFileDeathTest, SetLengthMappedReadWrite) {
262 MappedFile file;
263 ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
264 ASSERT_TRUE(file.MapReadWrite(10));
265 EXPECT_EQ(10, file.GetLength());
266 EXPECT_DEATH(file.SetLength(0), ".*");
267 }
268
269 TEST_F(MappedFileDeathTest, SetLengthMappedReadOnly) {
270 MappedFile file;
271 ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
272 ASSERT_TRUE(file.MapReadOnly());
273 EXPECT_EQ(kContent.size(), file.GetLength());
274 EXPECT_DEATH(file.SetLength(0), ".*");
275 }
276
277 TEST_F(MappedFileDeathTest, WriteMappedReadOnly) {
278 MappedFile file;
279 ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
280 ASSERT_TRUE(file.MapReadOnly());
281 char buf[10];
282 EXPECT_DEATH(file.Write(buf, 0, 0), ".*");
283 }
284
285 #endif
286
287 } // namespace unix_file
288