blob: 8f79d5dce9c0a824cd4a22bb186b0b037909f4f7 [file] [log] [blame]
Jiakai Zhanga2ba6962022-07-04 16:56:12 +01001/*
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
36namespace art {
37namespace artd {
38namespace {
39
40using ::aidl::com::android::server::art::FsPermission;
41using ::android::base::Error;
42using ::android::base::ReadFileToString;
43using ::android::base::Result;
44using ::android::base::WriteStringToFd;
45using ::android::base::WriteStringToFile;
46using ::android::base::testing::HasError;
47using ::android::base::testing::HasValue;
48using ::android::base::testing::Ok;
49using ::android::base::testing::WithMessage;
50using ::testing::ContainsRegex;
51using ::testing::IsEmpty;
52using ::testing::NotNull;
53
54void 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 `Commit`.
61class UncommittableFile : public NewFile {
62 public:
63 static Result<std::unique_ptr<UncommittableFile>> 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<UncommittableFile>(new UncommittableFile(std::move(*new_file)));
67 }
68
69 Result<void> Keep() override { return Error() << "Uncommittable file"; }
70
71 private:
72 explicit UncommittableFile(NewFile&& other) : NewFile(std::move(other)) {}
73};
74
75class FileUtilsTest : public CommonArtTest {
76 protected:
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
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
95TEST_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
109TEST_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
117TEST_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
126TEST_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
140TEST_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
154TEST_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
175TEST_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
199TEST_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
222TEST_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
250TEST_F(FileUtilsTest, NewFileCommitAllFailedToCommit) {
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 // Uncommittable file.
261 std::unique_ptr<NewFile> new_file_2 =
262 OR_FATAL(UncommittableFile::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("Uncommittable 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
280TEST_F(FileUtilsTest, NewFileCommitAllFailedToMoveOldFile) {
281 std::string file_1_path = scratch_dir_->GetPath() + "/file_1";
282 std::string file_2_path = scratch_dir_->GetPath() + "/file_2";
283 std::filesystem::create_directory(file_2_path);
284 std::string file_3_path = scratch_dir_->GetPath() + "/file_3";
285
286 ASSERT_TRUE(WriteStringToFile("old_file_1", file_1_path));
287 ASSERT_TRUE(WriteStringToFile("old_file_3", file_3_path)); // Extra file.
288
289 std::unique_ptr<NewFile> new_file_1 = OR_FATAL(NewFile::Create(file_1_path, fs_permission_));
290 std::unique_ptr<NewFile> new_file_2 = OR_FATAL(NewFile::Create(file_2_path, fs_permission_));
291
292 ASSERT_TRUE(WriteStringToFd("new_file_1", new_file_1->Fd()));
293 ASSERT_TRUE(WriteStringToFd("new_file_2", new_file_2->Fd()));
294
295 // file_2 is not movable because it is a directory.
296 EXPECT_THAT(NewFile::CommitAllOrAbandon({new_file_1.get(), new_file_2.get()}, {file_3_path}),
297 HasError(WithMessage(ContainsRegex("Old file '.*/file_2' is a directory"))));
298
299 // Old files are fine.
300 CheckContent(file_1_path, "old_file_1");
301 EXPECT_TRUE(std::filesystem::is_directory(file_2_path));
302 CheckContent(file_3_path, "old_file_3");
303
304 // New files are abandoned.
305 EXPECT_FALSE(std::filesystem::exists(new_file_1->TempPath()));
306 EXPECT_FALSE(std::filesystem::exists(new_file_2->TempPath()));
307}
308
309TEST_F(FileUtilsTest, BuildTempPath) {
310 EXPECT_EQ(NewFile::BuildTempPath("/a/b/original_path", "123456"),
311 "/a/b/original_path.123456.tmp");
312}
313
314TEST_F(FileUtilsTest, OpenFileForReading) {
315 std::string path = scratch_dir_->GetPath() + "/foo";
316 ASSERT_TRUE(WriteStringToFile("foo", path));
317
318 EXPECT_THAT(OpenFileForReading(path), HasValue(NotNull()));
319}
320
321TEST_F(FileUtilsTest, OpenFileForReadingFailed) {
322 std::string path = scratch_dir_->GetPath() + "/foo";
323
324 EXPECT_THAT(OpenFileForReading(path),
325 HasError(WithMessage(ContainsRegex("Failed to open file .*/foo"))));
326}
327
Jiakai Zhang3aaecf02022-08-10 15:35:28 +0100328TEST_F(FileUtilsTest, FileFsPermissionToMode) {
329 EXPECT_EQ(FileFsPermissionToMode(FsPermission{}), S_IRUSR | S_IWUSR | S_IRGRP);
330 EXPECT_EQ(FileFsPermissionToMode(FsPermission{.isOtherReadable = true}),
Jiakai Zhanga2ba6962022-07-04 16:56:12 +0100331 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
Jiakai Zhang3aaecf02022-08-10 15:35:28 +0100332 EXPECT_EQ(FileFsPermissionToMode(FsPermission{.isOtherExecutable = true}),
Jiakai Zhanga2ba6962022-07-04 16:56:12 +0100333 S_IRUSR | S_IWUSR | S_IRGRP | S_IXOTH);
Jiakai Zhang3aaecf02022-08-10 15:35:28 +0100334 EXPECT_EQ(
335 FileFsPermissionToMode(FsPermission{.isOtherReadable = true, .isOtherExecutable = true}),
336 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | S_IXOTH);
337}
338
339TEST_F(FileUtilsTest, DirFsPermissionToMode) {
340 EXPECT_EQ(DirFsPermissionToMode(FsPermission{}), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP);
341 EXPECT_EQ(DirFsPermissionToMode(FsPermission{.isOtherReadable = true}),
342 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH);
343 EXPECT_EQ(DirFsPermissionToMode(FsPermission{.isOtherExecutable = true}),
344 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IXOTH);
345 EXPECT_EQ(DirFsPermissionToMode(FsPermission{.isOtherReadable = true, .isOtherExecutable = true}),
346 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
Jiakai Zhanga2ba6962022-07-04 16:56:12 +0100347}
348
349} // namespace
350} // namespace artd
351} // namespace art