1 /*
2 * Copyright (C) 2021 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 <fcntl.h>
18 #include <string.h>
19 #include <sys/stat.h>
20
21 #include <iosfwd>
22 #include <memory>
23 #include <string>
24 #include <vector>
25
26 #include "android-base/stringprintf.h"
27 #include "base/bit_utils.h"
28 #include "base/common_art_test.h"
29 #include "base/os.h"
30 #include "base/unix_file/fd_file.h"
31 #include "odr_fs_utils.h"
32
33 namespace art {
34 namespace odrefresh {
35
36 class OdrFsUtilsTest : public CommonArtTest {};
37 namespace {
38
CreateFile(const char * file_path,size_t bytes)39 static bool CreateFile(const char* file_path, size_t bytes) {
40 std::unique_ptr<File> fp(OS::CreateEmptyFile(file_path));
41 if (!fp) {
42 return false;
43 }
44
45 std::vector<char> buffer(bytes, 0xa5);
46 if (!fp->WriteFully(buffer.data(), buffer.size())) {
47 fp->Erase();
48 return false;
49 }
50
51 if (fp->FlushClose() != 0) {
52 fp->Erase();
53 return false;
54 }
55
56 return true;
57 }
58
59 } // namespace
60
TEST_F(OdrFsUtilsTest,CleanDirectory)61 TEST_F(OdrFsUtilsTest, CleanDirectory) {
62 ScratchDir scratch_dir(/*keep_files=*/false);
63
64 // Create some sub-directories and files
65 const std::string dir_paths[] = {
66 scratch_dir.GetPath() + "/a",
67 scratch_dir.GetPath() + "/b",
68 scratch_dir.GetPath() + "/b/c",
69 scratch_dir.GetPath() + "/d"
70 };
71 for (const auto& dir_path : dir_paths) {
72 ASSERT_EQ(0, mkdir(dir_path.c_str(), S_IRWXU));
73 }
74
75 const std::string file_paths[] = {
76 scratch_dir.GetPath() + "/zero.txt",
77 scratch_dir.GetPath() + "/a/one.txt",
78 scratch_dir.GetPath() + "/b/two.txt",
79 scratch_dir.GetPath() + "/b/c/three.txt",
80 scratch_dir.GetPath() + "/b/c/four.txt",
81 };
82 for (const auto& file_path : file_paths) {
83 ASSERT_TRUE(CreateFile(file_path.c_str(), 4096));
84 }
85
86 // Clean all files and sub-directories
87 ASSERT_TRUE(CleanDirectory(scratch_dir.GetPath()));
88
89 // Check nothing we created remains.
90 for (const auto& dir_path : dir_paths) {
91 ASSERT_FALSE(OS::DirectoryExists(dir_path.c_str()));
92 }
93
94 for (const auto& file_path : file_paths) {
95 ASSERT_FALSE(OS::FileExists(file_path.c_str(), true));
96 }
97 }
98
TEST_F(OdrFsUtilsTest,EnsureDirectoryExistsBadPath)99 TEST_F(OdrFsUtilsTest, EnsureDirectoryExistsBadPath) {
100 // Pick a path where not even a root test runner can write.
101 ASSERT_FALSE(EnsureDirectoryExists("/proc/unlikely/to/be/writable"));
102 }
103
TEST_F(OdrFsUtilsTest,EnsureDirectoryExistsEmptyPath)104 TEST_F(OdrFsUtilsTest, EnsureDirectoryExistsEmptyPath) {
105 ASSERT_FALSE(EnsureDirectoryExists(""));
106 }
107
TEST_F(OdrFsUtilsTest,EnsureDirectoryExistsRelativePath)108 TEST_F(OdrFsUtilsTest, EnsureDirectoryExistsRelativePath) {
109 ASSERT_FALSE(EnsureDirectoryExists("a/b/c"));
110 }
111
TEST_F(OdrFsUtilsTest,EnsureDirectoryExistsSubDirs)112 TEST_F(OdrFsUtilsTest, EnsureDirectoryExistsSubDirs) {
113 ScratchDir scratch_dir(/*keep_files=*/false);
114
115 const char* relative_sub_dirs[] = {"a", "b/c", "d/e/f/"};
116 for (const char* relative_sub_dir : relative_sub_dirs) {
117 ASSERT_TRUE(EnsureDirectoryExists(scratch_dir.GetPath() + "/" + relative_sub_dir));
118 }
119 }
120
TEST_F(OdrFsUtilsTest,DISABLED_GetUsedSpace)121 TEST_F(OdrFsUtilsTest, DISABLED_GetUsedSpace) {
122 static constexpr size_t kFirstFileBytes = 1;
123 static constexpr size_t kSecondFileBytes = 16111;
124 static constexpr size_t kBytesPerBlock = 512;
125
126 ScratchDir scratch_dir(/*keep_files=*/false);
127
128 const std::string first_file_path = scratch_dir.GetPath() + "/1.dat";
129 ASSERT_TRUE(CreateFile(first_file_path.c_str(), kFirstFileBytes));
130
131 struct stat sb;
132 ASSERT_EQ(0, stat(first_file_path.c_str(), &sb));
133 ASSERT_EQ(kFirstFileBytes, static_cast<decltype(kFirstFileBytes)>(sb.st_size));
134
135 uint64_t bytes_used = 0;
136 ASSERT_TRUE(GetUsedSpace(scratch_dir.GetPath().c_str(), &bytes_used));
137
138 const std::string second_file_path = scratch_dir.GetPath() + "/2.dat";
139 ASSERT_TRUE(CreateFile(second_file_path.c_str(), kSecondFileBytes));
140
141 ASSERT_TRUE(GetUsedSpace(scratch_dir.GetPath().c_str(), &bytes_used));
142 uint64_t expected_bytes_used = RoundUp(kFirstFileBytes, sb.st_blocks * kBytesPerBlock) +
143 RoundUp(kSecondFileBytes, sb.st_blocks * kBytesPerBlock);
144 ASSERT_EQ(expected_bytes_used, bytes_used);
145
146 const std::string sub_dir_path = scratch_dir.GetPath() + "/sub";
147 ASSERT_TRUE(EnsureDirectoryExists(sub_dir_path));
148 for (size_t i = 1; i < 32768; i *= 17) {
149 const std::string path = android::base::StringPrintf("%s/%zu", sub_dir_path.c_str(), i);
150 ASSERT_TRUE(CreateFile(path.c_str(), i));
151 expected_bytes_used += RoundUp(i, sb.st_blocks * kBytesPerBlock);
152 ASSERT_TRUE(GetUsedSpace(scratch_dir.GetPath().c_str(), &bytes_used));
153 ASSERT_EQ(expected_bytes_used, bytes_used);
154 }
155 }
156
TEST_F(OdrFsUtilsTest,GetUsedSpaceBadPath)157 TEST_F(OdrFsUtilsTest, GetUsedSpaceBadPath) {
158 ScratchDir scratch_dir(/*keep_files=*/false);
159 const std::string bad_path = scratch_dir.GetPath() + "/bad_path";
160 uint64_t bytes_used = ~0ull;
161 ASSERT_TRUE(GetUsedSpace(bad_path, &bytes_used));
162 ASSERT_EQ(0ull, bytes_used);
163 }
164
165 } // namespace odrefresh
166 } // namespace art
167