1 /*
2 * Copyright (C) 2022 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 "file_utils.h"
18
19 #include <fcntl.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22
23 #include <filesystem>
24 #include <memory>
25 #include <string>
26
27 #include "aidl/com/android/server/art/FsPermission.h"
28 #include "android-base/errors.h"
29 #include "android-base/file.h"
30 #include "android-base/result-gmock.h"
31 #include "android-base/result.h"
32 #include "base/common_art_test.h"
33 #include "gmock/gmock.h"
34 #include "gtest/gtest.h"
35
36 namespace art {
37 namespace artd {
38 namespace {
39
40 using ::aidl::com::android::server::art::FsPermission;
41 using ::android::base::Error;
42 using ::android::base::ReadFileToString;
43 using ::android::base::Result;
44 using ::android::base::WriteStringToFd;
45 using ::android::base::WriteStringToFile;
46 using ::android::base::testing::HasError;
47 using ::android::base::testing::HasValue;
48 using ::android::base::testing::Ok;
49 using ::android::base::testing::WithMessage;
50 using ::testing::ContainsRegex;
51 using ::testing::IsEmpty;
52 using ::testing::NotNull;
53
CheckContent(const std::string & path,const std::string & expected_content)54 void CheckContent(const std::string& path, const std::string& expected_content) {
55 std::string actual_content;
56 ASSERT_TRUE(ReadFileToString(path, &actual_content));
57 EXPECT_EQ(actual_content, expected_content);
58 }
59
60 // A file that will always fail on `Keep`.
61 class UnkeepableFile : public NewFile {
62 public:
Create(const std::string & path,const FsPermission & fs_permission)63 static Result<std::unique_ptr<UnkeepableFile>> Create(const std::string& path,
64 const FsPermission& fs_permission) {
65 std::unique_ptr<NewFile> new_file = OR_RETURN(NewFile::Create(path, fs_permission));
66 return std::unique_ptr<UnkeepableFile>(new UnkeepableFile(std::move(*new_file)));
67 }
68
Keep()69 Result<void> Keep() override { return Error() << "Unkeepable file"; }
70
71 private:
UnkeepableFile(NewFile && other)72 explicit UnkeepableFile(NewFile&& other) : NewFile(std::move(other)) {}
73 };
74
75 class FileUtilsTest : public CommonArtTest {
76 protected:
SetUp()77 void SetUp() override {
78 CommonArtTest::SetUp();
79 scratch_dir_ = std::make_unique<ScratchDir>();
80 struct stat st;
81 ASSERT_EQ(stat(scratch_dir_->GetPath().c_str(), &st), 0);
82 fs_permission_ = FsPermission{.uid = static_cast<int32_t>(st.st_uid),
83 .gid = static_cast<int32_t>(st.st_gid)};
84 }
85
TearDown()86 void TearDown() override {
87 scratch_dir_.reset();
88 CommonArtTest::TearDown();
89 }
90
91 FsPermission fs_permission_;
92 std::unique_ptr<ScratchDir> scratch_dir_;
93 };
94
TEST_F(FileUtilsTest,NewFileCreate)95 TEST_F(FileUtilsTest, NewFileCreate) {
96 std::string path = scratch_dir_->GetPath() + "/file.tmp";
97
98 Result<std::unique_ptr<NewFile>> new_file = NewFile::Create(path, fs_permission_);
99 ASSERT_THAT(new_file, HasValue(NotNull()));
100 EXPECT_GE((*new_file)->Fd(), 0);
101 EXPECT_EQ((*new_file)->FinalPath(), path);
102 EXPECT_THAT((*new_file)->TempPath(), Not(IsEmpty()));
103 EXPECT_THAT((*new_file)->TempId(), Not(IsEmpty()));
104
105 EXPECT_FALSE(std::filesystem::exists((*new_file)->FinalPath()));
106 EXPECT_TRUE(std::filesystem::exists((*new_file)->TempPath()));
107 }
108
TEST_F(FileUtilsTest,NewFileCreateNonExistentDir)109 TEST_F(FileUtilsTest, NewFileCreateNonExistentDir) {
110 std::string path = scratch_dir_->GetPath() + "/non_existent_dir/file.tmp";
111
112 EXPECT_THAT(NewFile::Create(path, fs_permission_),
113 HasError(WithMessage(
114 ContainsRegex("Failed to create temp file for .*/non_existent_dir/file.tmp"))));
115 }
116
TEST_F(FileUtilsTest,NewFileExplicitCleanup)117 TEST_F(FileUtilsTest, NewFileExplicitCleanup) {
118 std::string path = scratch_dir_->GetPath() + "/file.tmp";
119 std::unique_ptr<NewFile> new_file = OR_FATAL(NewFile::Create(path, fs_permission_));
120 new_file->Cleanup();
121
122 EXPECT_FALSE(std::filesystem::exists(path));
123 EXPECT_FALSE(std::filesystem::exists(new_file->TempPath()));
124 }
125
TEST_F(FileUtilsTest,NewFileImplicitCleanup)126 TEST_F(FileUtilsTest, NewFileImplicitCleanup) {
127 std::string path = scratch_dir_->GetPath() + "/file.tmp";
128 std::string temp_path;
129
130 // Cleanup on object destruction.
131 {
132 std::unique_ptr<NewFile> new_file = OR_FATAL(NewFile::Create(path, fs_permission_));
133 temp_path = new_file->TempPath();
134 }
135
136 EXPECT_FALSE(std::filesystem::exists(path));
137 EXPECT_FALSE(std::filesystem::exists(temp_path));
138 }
139
TEST_F(FileUtilsTest,NewFileCommit)140 TEST_F(FileUtilsTest, NewFileCommit) {
141 std::string path = scratch_dir_->GetPath() + "/file.tmp";
142 std::string temp_path;
143
144 {
145 std::unique_ptr<NewFile> new_file = OR_FATAL(NewFile::Create(path, fs_permission_));
146 temp_path = new_file->TempPath();
147 new_file->CommitOrAbandon();
148 }
149
150 EXPECT_TRUE(std::filesystem::exists(path));
151 EXPECT_FALSE(std::filesystem::exists(temp_path));
152 }
153
TEST_F(FileUtilsTest,NewFileCommitAllNoOldFile)154 TEST_F(FileUtilsTest, NewFileCommitAllNoOldFile) {
155 std::string file_1_path = scratch_dir_->GetPath() + "/file_1";
156 std::string file_2_path = scratch_dir_->GetPath() + "/file_2";
157
158 std::unique_ptr<NewFile> new_file_1 = OR_FATAL(NewFile::Create(file_1_path, fs_permission_));
159 std::unique_ptr<NewFile> new_file_2 = OR_FATAL(NewFile::Create(file_2_path, fs_permission_));
160
161 ASSERT_TRUE(WriteStringToFd("new_file_1", new_file_1->Fd()));
162 ASSERT_TRUE(WriteStringToFd("new_file_2", new_file_2->Fd()));
163
164 EXPECT_THAT(NewFile::CommitAllOrAbandon({new_file_1.get(), new_file_2.get()}), Ok());
165
166 // New files are committed.
167 CheckContent(file_1_path, "new_file_1");
168 CheckContent(file_2_path, "new_file_2");
169
170 // New files are no longer at the temporary paths.
171 EXPECT_FALSE(std::filesystem::exists(new_file_1->TempPath()));
172 EXPECT_FALSE(std::filesystem::exists(new_file_2->TempPath()));
173 }
174
TEST_F(FileUtilsTest,NewFileCommitAllReplacesOldFiles)175 TEST_F(FileUtilsTest, NewFileCommitAllReplacesOldFiles) {
176 std::string file_1_path = scratch_dir_->GetPath() + "/file_1";
177 std::string file_2_path = scratch_dir_->GetPath() + "/file_2";
178
179 ASSERT_TRUE(WriteStringToFile("old_file_1", file_1_path));
180 ASSERT_TRUE(WriteStringToFile("old_file_2", file_2_path));
181
182 std::unique_ptr<NewFile> new_file_1 = OR_FATAL(NewFile::Create(file_1_path, fs_permission_));
183 std::unique_ptr<NewFile> new_file_2 = OR_FATAL(NewFile::Create(file_2_path, fs_permission_));
184
185 ASSERT_TRUE(WriteStringToFd("new_file_1", new_file_1->Fd()));
186 ASSERT_TRUE(WriteStringToFd("new_file_2", new_file_2->Fd()));
187
188 EXPECT_THAT(NewFile::CommitAllOrAbandon({new_file_1.get(), new_file_2.get()}), Ok());
189
190 // New files are committed.
191 CheckContent(file_1_path, "new_file_1");
192 CheckContent(file_2_path, "new_file_2");
193
194 // New files are no longer at the temporary paths.
195 EXPECT_FALSE(std::filesystem::exists(new_file_1->TempPath()));
196 EXPECT_FALSE(std::filesystem::exists(new_file_2->TempPath()));
197 }
198
TEST_F(FileUtilsTest,NewFileCommitAllReplacesLessOldFiles)199 TEST_F(FileUtilsTest, NewFileCommitAllReplacesLessOldFiles) {
200 std::string file_1_path = scratch_dir_->GetPath() + "/file_1";
201 std::string file_2_path = scratch_dir_->GetPath() + "/file_2";
202
203 ASSERT_TRUE(WriteStringToFile("old_file_1", file_1_path)); // No old_file_2.
204
205 std::unique_ptr<NewFile> new_file_1 = OR_FATAL(NewFile::Create(file_1_path, fs_permission_));
206 std::unique_ptr<NewFile> new_file_2 = OR_FATAL(NewFile::Create(file_2_path, fs_permission_));
207
208 ASSERT_TRUE(WriteStringToFd("new_file_1", new_file_1->Fd()));
209 ASSERT_TRUE(WriteStringToFd("new_file_2", new_file_2->Fd()));
210
211 EXPECT_THAT(NewFile::CommitAllOrAbandon({new_file_1.get(), new_file_2.get()}), Ok());
212
213 // New files are committed.
214 CheckContent(file_1_path, "new_file_1");
215 CheckContent(file_2_path, "new_file_2");
216
217 // New files are no longer at the temporary paths.
218 EXPECT_FALSE(std::filesystem::exists(new_file_1->TempPath()));
219 EXPECT_FALSE(std::filesystem::exists(new_file_2->TempPath()));
220 }
221
TEST_F(FileUtilsTest,NewFileCommitAllReplacesMoreOldFiles)222 TEST_F(FileUtilsTest, NewFileCommitAllReplacesMoreOldFiles) {
223 std::string file_1_path = scratch_dir_->GetPath() + "/file_1";
224 std::string file_2_path = scratch_dir_->GetPath() + "/file_2";
225 std::string file_3_path = scratch_dir_->GetPath() + "/file_3";
226
227 ASSERT_TRUE(WriteStringToFile("old_file_1", file_1_path));
228 ASSERT_TRUE(WriteStringToFile("old_file_2", file_2_path));
229 ASSERT_TRUE(WriteStringToFile("old_file_3", file_3_path)); // Extra file.
230
231 std::unique_ptr<NewFile> new_file_1 = OR_FATAL(NewFile::Create(file_1_path, fs_permission_));
232 std::unique_ptr<NewFile> new_file_2 = OR_FATAL(NewFile::Create(file_2_path, fs_permission_));
233
234 ASSERT_TRUE(WriteStringToFd("new_file_1", new_file_1->Fd()));
235 ASSERT_TRUE(WriteStringToFd("new_file_2", new_file_2->Fd()));
236
237 EXPECT_THAT(NewFile::CommitAllOrAbandon({new_file_1.get(), new_file_2.get()}, {file_3_path}),
238 Ok());
239
240 // New files are committed.
241 CheckContent(file_1_path, "new_file_1");
242 CheckContent(file_2_path, "new_file_2");
243 EXPECT_FALSE(std::filesystem::exists(file_3_path)); // Extra file removed.
244
245 // New files are no longer at the temporary paths.
246 EXPECT_FALSE(std::filesystem::exists(new_file_1->TempPath()));
247 EXPECT_FALSE(std::filesystem::exists(new_file_2->TempPath()));
248 }
249
TEST_F(FileUtilsTest,NewFileCommitAllFailedToKeep)250 TEST_F(FileUtilsTest, NewFileCommitAllFailedToKeep) {
251 std::string file_1_path = scratch_dir_->GetPath() + "/file_1";
252 std::string file_2_path = scratch_dir_->GetPath() + "/file_2";
253 std::string file_3_path = scratch_dir_->GetPath() + "/file_3";
254
255 ASSERT_TRUE(WriteStringToFile("old_file_1", file_1_path));
256 ASSERT_TRUE(WriteStringToFile("old_file_2", file_2_path));
257 ASSERT_TRUE(WriteStringToFile("old_file_3", file_3_path)); // Extra file.
258
259 std::unique_ptr<NewFile> new_file_1 = OR_FATAL(NewFile::Create(file_1_path, fs_permission_));
260 // Unkeepable file.
261 std::unique_ptr<NewFile> new_file_2 =
262 OR_FATAL(UnkeepableFile::Create(file_2_path, fs_permission_));
263
264 ASSERT_TRUE(WriteStringToFd("new_file_1", new_file_1->Fd()));
265 ASSERT_TRUE(WriteStringToFd("new_file_2", new_file_2->Fd()));
266
267 EXPECT_THAT(NewFile::CommitAllOrAbandon({new_file_1.get(), new_file_2.get()}, {file_3_path}),
268 HasError(WithMessage("Unkeepable file")));
269
270 // Old files are fine.
271 CheckContent(file_1_path, "old_file_1");
272 CheckContent(file_2_path, "old_file_2");
273 CheckContent(file_3_path, "old_file_3");
274
275 // New files are abandoned.
276 EXPECT_FALSE(std::filesystem::exists(new_file_1->TempPath()));
277 EXPECT_FALSE(std::filesystem::exists(new_file_2->TempPath()));
278 }
279
TEST_F(FileUtilsTest,NewFileCommitAllFailedToCommit)280 TEST_F(FileUtilsTest, NewFileCommitAllFailedToCommit) {
281 std::string dir_1_path = scratch_dir_->GetPath() + "/dir_1";
282 std::string dir_2_path = scratch_dir_->GetPath() + "/dir_2";
283
284 ASSERT_TRUE(std::filesystem::create_directory(dir_1_path));
285 ASSERT_TRUE(std::filesystem::create_directory(dir_2_path));
286
287 std::string file_1_path = dir_1_path + "/file_1";
288 std::string file_2_path = dir_2_path + "/file_2";
289
290 std::unique_ptr<NewFile> new_file_1 = OR_FATAL(NewFile::Create(file_1_path, fs_permission_));
291 std::unique_ptr<NewFile> new_file_2 = OR_FATAL(NewFile::Create(file_2_path, fs_permission_));
292
293 ASSERT_TRUE(WriteStringToFd("new_file_1", new_file_1->Fd()));
294 ASSERT_TRUE(WriteStringToFd("new_file_2", new_file_2->Fd()));
295
296 {
297 // Make `new_file_2` fail to commit.
298 auto scoped_inaccessible = ScopedInaccessible(dir_2_path);
299 std::filesystem::permissions(
300 dir_2_path, std::filesystem::perms::owner_exec, std::filesystem::perm_options::add);
301 auto scoped_unroot = ScopedUnroot();
302
303 EXPECT_THAT(NewFile::CommitAllOrAbandon({new_file_1.get(), new_file_2.get()}),
304 HasError(WithMessage(ContainsRegex("Failed to move file .*file_2.*"))));
305 }
306
307 // Files are abandoned at best effort. File 1 is abandoned, but file 2 cannot be abandoned due to
308 // permission denied.
309 EXPECT_FALSE(std::filesystem::exists(new_file_1->TempPath()));
310 EXPECT_FALSE(std::filesystem::exists(new_file_1->FinalPath()));
311 EXPECT_TRUE(std::filesystem::exists(new_file_2->TempPath()));
312 EXPECT_FALSE(std::filesystem::exists(new_file_2->FinalPath()));
313 }
314
TEST_F(FileUtilsTest,NewFileCommitAllFailedToMoveOldFile)315 TEST_F(FileUtilsTest, NewFileCommitAllFailedToMoveOldFile) {
316 std::string file_1_path = scratch_dir_->GetPath() + "/file_1";
317 std::string file_2_path = scratch_dir_->GetPath() + "/file_2";
318 std::filesystem::create_directory(file_2_path);
319 std::string file_3_path = scratch_dir_->GetPath() + "/file_3";
320
321 ASSERT_TRUE(WriteStringToFile("old_file_1", file_1_path));
322 ASSERT_TRUE(WriteStringToFile("old_file_3", file_3_path)); // Extra file.
323
324 std::unique_ptr<NewFile> new_file_1 = OR_FATAL(NewFile::Create(file_1_path, fs_permission_));
325 std::unique_ptr<NewFile> new_file_2 = OR_FATAL(NewFile::Create(file_2_path, fs_permission_));
326
327 ASSERT_TRUE(WriteStringToFd("new_file_1", new_file_1->Fd()));
328 ASSERT_TRUE(WriteStringToFd("new_file_2", new_file_2->Fd()));
329
330 // file_2 is not movable because it is a directory.
331 EXPECT_THAT(NewFile::CommitAllOrAbandon({new_file_1.get(), new_file_2.get()}, {file_3_path}),
332 HasError(WithMessage(ContainsRegex("Old file '.*/file_2' is a directory"))));
333
334 // Old files are fine.
335 CheckContent(file_1_path, "old_file_1");
336 EXPECT_TRUE(std::filesystem::is_directory(file_2_path));
337 CheckContent(file_3_path, "old_file_3");
338
339 // New files are abandoned.
340 EXPECT_FALSE(std::filesystem::exists(new_file_1->TempPath()));
341 EXPECT_FALSE(std::filesystem::exists(new_file_2->TempPath()));
342 }
343
TEST_F(FileUtilsTest,BuildTempPath)344 TEST_F(FileUtilsTest, BuildTempPath) {
345 EXPECT_EQ(NewFile::BuildTempPath("/a/b/original_path", "123456"),
346 "/a/b/original_path.123456.tmp");
347 }
348
TEST_F(FileUtilsTest,OpenFileForReading)349 TEST_F(FileUtilsTest, OpenFileForReading) {
350 std::string path = scratch_dir_->GetPath() + "/foo";
351 ASSERT_TRUE(WriteStringToFile("foo", path));
352
353 EXPECT_THAT(OpenFileForReading(path), HasValue(NotNull()));
354 }
355
TEST_F(FileUtilsTest,OpenFileForReadingFailed)356 TEST_F(FileUtilsTest, OpenFileForReadingFailed) {
357 std::string path = scratch_dir_->GetPath() + "/foo";
358
359 EXPECT_THAT(OpenFileForReading(path),
360 HasError(WithMessage(ContainsRegex("Failed to open file .*/foo"))));
361 }
362
TEST_F(FileUtilsTest,FileFsPermissionToMode)363 TEST_F(FileUtilsTest, FileFsPermissionToMode) {
364 EXPECT_EQ(FileFsPermissionToMode(FsPermission{}), S_IRUSR | S_IWUSR | S_IRGRP);
365 EXPECT_EQ(FileFsPermissionToMode(FsPermission{.isOtherReadable = true}),
366 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
367 EXPECT_EQ(FileFsPermissionToMode(FsPermission{.isOtherExecutable = true}),
368 S_IRUSR | S_IWUSR | S_IRGRP | S_IXOTH);
369 EXPECT_EQ(
370 FileFsPermissionToMode(FsPermission{.isOtherReadable = true, .isOtherExecutable = true}),
371 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | S_IXOTH);
372 }
373
TEST_F(FileUtilsTest,DirFsPermissionToMode)374 TEST_F(FileUtilsTest, DirFsPermissionToMode) {
375 EXPECT_EQ(DirFsPermissionToMode(FsPermission{}), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP);
376 EXPECT_EQ(DirFsPermissionToMode(FsPermission{.isOtherReadable = true}),
377 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH);
378 EXPECT_EQ(DirFsPermissionToMode(FsPermission{.isOtherExecutable = true}),
379 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IXOTH);
380 EXPECT_EQ(DirFsPermissionToMode(FsPermission{.isOtherReadable = true, .isOtherExecutable = true}),
381 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
382 }
383
384 } // namespace
385 } // namespace artd
386 } // namespace art
387