1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "brillo/files/file_util_test.h"
6 
7 #include <base/files/file_util.h>
8 #include <base/rand_util.h>
9 #include <base/stl_util.h>
10 #include <base/strings/string_number_conversions.h>
11 #include <brillo/files/file_util.h>
12 #include <brillo/files/safe_fd.h>
13 
14 namespace brillo {
15 
16 #define TO_STRING_HELPER(x)      \
17   case brillo::SafeFD::Error::x: \
18     return #x;
to_string(brillo::SafeFD::Error err)19 std::string to_string(brillo::SafeFD::Error err) {
20   switch (err) {
21     TO_STRING_HELPER(kNoError)
22     TO_STRING_HELPER(kBadArgument)
23     TO_STRING_HELPER(kNotInitialized)
24     TO_STRING_HELPER(kIOError)
25     TO_STRING_HELPER(kDoesNotExist)
26     TO_STRING_HELPER(kSymlinkDetected)
27     TO_STRING_HELPER(kWrongType)
28     TO_STRING_HELPER(kWrongUID)
29     TO_STRING_HELPER(kWrongGID)
30     TO_STRING_HELPER(kWrongPermissions)
31     TO_STRING_HELPER(kExceededMaximum)
32     default:
33       return std::string("unknown (") + std::to_string(static_cast<int>(err)) +
34              ")";
35   }
36 }
37 #undef TO_STRING_HELPER
38 
operator <<(std::ostream & os,const brillo::SafeFD::Error err)39 std::ostream& operator<<(std::ostream& os, const brillo::SafeFD::Error err) {
40   return os << to_string(err);  // whatever needed to print bar to os
41 }
42 
GetRandomSuffix()43 std::string GetRandomSuffix() {
44   const int kBufferSize = 6;
45   unsigned char buffer[kBufferSize];
46   base::RandBytes(buffer, base::size(buffer));
47   return base::HexEncode(buffer, base::size(buffer));
48 }
49 
SetUpTestCase()50 void FileTest::SetUpTestCase() {
51   umask(0);
52 }
53 
FileTest()54 FileTest::FileTest() {
55   CHECK(temp_dir_.CreateUniqueTempDir()) << strerror(errno);
56   sub_dir_path_ = temp_dir_.GetPath().Append(kSubdirName);
57   file_path_ = sub_dir_path_.Append(kFileName);
58 
59   std::string path = temp_dir_.GetPath().value();
60   temp_dir_path_.reserve(path.size() + 1);
61   temp_dir_path_.assign(temp_dir_.GetPath().value().begin(),
62                         temp_dir_.GetPath().value().end());
63   temp_dir_path_.push_back('\0');
64 
65   CHECK_EQ(chmod(temp_dir_path_.data(), SafeFD::kDefaultDirPermissions), 0);
66   SafeFD::SetRootPathForTesting(temp_dir_path_.data());
67   root_ = SafeFD::Root().first;
68   CHECK(root_.is_valid());
69 }
70 
SetupSubdir()71 bool FileTest::SetupSubdir() {
72   if (!base::CreateDirectory(sub_dir_path_)) {
73     PLOG(ERROR) << "Failed to create '" << sub_dir_path_.value() << "'";
74     return false;
75   }
76   if (chmod(sub_dir_path_.value().c_str(), SafeFD::kDefaultDirPermissions) !=
77       0) {
78     PLOG(ERROR) << "Failed to set permissions of '" << sub_dir_path_.value()
79                 << "'";
80     return false;
81   }
82   return true;
83 }
84 
SetupSymlinks()85 bool FileTest::SetupSymlinks() {
86   symlink_file_path_ = temp_dir_.GetPath().Append(kSymbolicFileName);
87   symlink_dir_path_ = temp_dir_.GetPath().Append(kSymbolicDirName);
88   if (!base::CreateSymbolicLink(file_path_, symlink_file_path_)) {
89     PLOG(ERROR) << "Failed to create symlink to '" << symlink_file_path_.value()
90                 << "'";
91     return false;
92   }
93   if (!base::CreateSymbolicLink(temp_dir_.GetPath(), symlink_dir_path_)) {
94     PLOG(ERROR) << "Failed to create symlink to'" << symlink_dir_path_.value()
95                 << "'";
96     return false;
97   }
98   return true;
99 }
100 
WriteFile(const std::string & contents)101 bool FileTest::WriteFile(const std::string& contents) {
102   if (!SetupSubdir()) {
103     return false;
104   }
105   if (contents.length() !=
106       base::WriteFile(file_path_, contents.c_str(), contents.length())) {
107     PLOG(ERROR) << "base::WriteFile failed";
108     return false;
109   }
110   if (chmod(file_path_.value().c_str(), SafeFD::kDefaultFilePermissions) != 0) {
111     PLOG(ERROR) << "chmod failed";
112     return false;
113   }
114   return true;
115 }
116 
ExpectFileContains(const std::string & contents)117 void FileTest::ExpectFileContains(const std::string& contents) {
118   EXPECT_TRUE(base::PathExists(file_path_));
119   std::string new_contents;
120   EXPECT_TRUE(base::ReadFileToString(file_path_, &new_contents));
121   EXPECT_EQ(contents, new_contents);
122 }
123 
ExpectPermissions(base::FilePath path,int permissions)124 void FileTest::ExpectPermissions(base::FilePath path, int permissions) {
125   int actual_permissions = 0;
126   // This breaks out of the ExpectPermissions() call but not the test case.
127   ASSERT_TRUE(base::GetPosixFilePermissions(path, &actual_permissions));
128   EXPECT_EQ(permissions, actual_permissions);
129 }
130 
131 // Creates a file with a random name in the temporary directory.
GetTempName()132 base::FilePath FileTest::GetTempName() {
133   return temp_dir_.GetPath().Append(GetRandomSuffix());
134 }
135 
136 constexpr char FileTest::kFileName[];
137 constexpr char FileTest::kSubdirName[];
138 constexpr char FileTest::kSymbolicFileName[];
139 constexpr char FileTest::kSymbolicDirName[];
140 
141 class FileUtilTest : public FileTest {};
142 
TEST_F(FileUtilTest,GetFDPath_SimpleSuccess)143 TEST_F(FileUtilTest, GetFDPath_SimpleSuccess) {
144   EXPECT_EQ(GetFDPath(root_.get()), temp_dir_.GetPath());
145 }
146 
TEST_F(FileUtilTest,GetFDPath_BadFD)147 TEST_F(FileUtilTest, GetFDPath_BadFD) {
148   base::FilePath path = GetFDPath(-1);
149   EXPECT_TRUE(path.empty());
150 }
151 
TEST_F(FileUtilTest,OpenOrRemakeDir_SimpleSuccess)152 TEST_F(FileUtilTest, OpenOrRemakeDir_SimpleSuccess) {
153   SafeFD::Error err;
154   SafeFD dir;
155 
156   std::tie(dir, err) = root_.OpenExistingDir(temp_dir_.GetPath());
157   EXPECT_EQ(err, SafeFD::Error::kNoError);
158   ASSERT_TRUE(dir.is_valid());
159 
160   SafeFD subdir;
161   std::tie(subdir, err) = OpenOrRemakeDir(&dir, kSubdirName);
162   EXPECT_EQ(err, SafeFD::Error::kNoError);
163   EXPECT_TRUE(subdir.is_valid());
164 }
165 
TEST_F(FileUtilTest,OpenOrRemakeDir_SuccessAfterRetry)166 TEST_F(FileUtilTest, OpenOrRemakeDir_SuccessAfterRetry) {
167   ASSERT_NE(base::WriteFile(sub_dir_path_, "", 0), -1);
168   SafeFD::Error err;
169   SafeFD dir;
170 
171   std::tie(dir, err) = root_.OpenExistingDir(temp_dir_.GetPath());
172   EXPECT_EQ(err, SafeFD::Error::kNoError);
173   ASSERT_TRUE(dir.is_valid());
174 
175   SafeFD subdir;
176   std::tie(subdir, err) = OpenOrRemakeDir(&dir, kSubdirName);
177   EXPECT_EQ(err, SafeFD::Error::kNoError);
178   EXPECT_TRUE(subdir.is_valid());
179 }
180 
TEST_F(FileUtilTest,OpenOrRemakeDir_BadArgument)181 TEST_F(FileUtilTest, OpenOrRemakeDir_BadArgument) {
182   SafeFD::Error err;
183   SafeFD dir;
184 
185   std::tie(dir, err) = root_.OpenExistingDir(temp_dir_.GetPath());
186   EXPECT_EQ(err, SafeFD::Error::kNoError);
187   ASSERT_TRUE(dir.is_valid());
188 
189   SafeFD subdir;
190   std::tie(subdir, err) = OpenOrRemakeDir(&dir, ".");
191   EXPECT_EQ(err, SafeFD::Error::kBadArgument);
192   EXPECT_FALSE(subdir.is_valid());
193   std::tie(subdir, err) = OpenOrRemakeDir(&dir, "..");
194   EXPECT_EQ(err, SafeFD::Error::kBadArgument);
195   EXPECT_FALSE(subdir.is_valid());
196   std::tie(subdir, err) = OpenOrRemakeDir(&dir, "a/a");
197   EXPECT_EQ(err, SafeFD::Error::kBadArgument);
198   EXPECT_FALSE(subdir.is_valid());
199 }
200 
TEST_F(FileUtilTest,OpenOrRemakeDir_NotInitialized)201 TEST_F(FileUtilTest, OpenOrRemakeDir_NotInitialized) {
202   SafeFD::Error err;
203   SafeFD dir;
204 
205   SafeFD subdir;
206   std::tie(subdir, err) = OpenOrRemakeDir(&dir, kSubdirName);
207   EXPECT_EQ(err, SafeFD::Error::kNotInitialized);
208   EXPECT_FALSE(subdir.is_valid());
209 }
210 
TEST_F(FileUtilTest,OpenOrRemakeDir_IOError)211 TEST_F(FileUtilTest, OpenOrRemakeDir_IOError) {
212   SafeFD::Error err;
213   SafeFD dir;
214 
215   std::tie(dir, err) = root_.OpenExistingDir(temp_dir_.GetPath());
216   EXPECT_EQ(err, SafeFD::Error::kNoError);
217   ASSERT_TRUE(dir.is_valid());
218   ASSERT_EQ(chmod(temp_dir_path_.data(), 0000), 0);
219 
220   SafeFD subdir;
221   std::tie(subdir, err) = OpenOrRemakeDir(&dir, kSubdirName);
222   EXPECT_EQ(err, SafeFD::Error::kIOError);
223   EXPECT_FALSE(subdir.is_valid());
224 }
225 
TEST_F(FileUtilTest,OpenOrRemakeFile_SimpleSuccess)226 TEST_F(FileUtilTest, OpenOrRemakeFile_SimpleSuccess) {
227   ASSERT_TRUE(SetupSubdir());
228   SafeFD::Error err;
229   SafeFD dir;
230 
231   std::tie(dir, err) = root_.OpenExistingDir(sub_dir_path_);
232   EXPECT_EQ(err, SafeFD::Error::kNoError);
233   ASSERT_TRUE(dir.is_valid());
234 
235   SafeFD file;
236   std::tie(file, err) = OpenOrRemakeFile(&dir, kFileName);
237   EXPECT_EQ(err, SafeFD::Error::kNoError);
238   EXPECT_TRUE(file.is_valid());
239 }
240 
TEST_F(FileUtilTest,OpenOrRemakeFile_SuccessAfterRetry)241 TEST_F(FileUtilTest, OpenOrRemakeFile_SuccessAfterRetry) {
242   ASSERT_TRUE(SetupSubdir());
243   ASSERT_TRUE(base::CreateDirectory(file_path_));
244   SafeFD::Error err;
245   SafeFD dir;
246 
247   std::tie(dir, err) = root_.OpenExistingDir(sub_dir_path_);
248   EXPECT_EQ(err, SafeFD::Error::kNoError);
249   ASSERT_TRUE(dir.is_valid());
250 
251   SafeFD file;
252   std::tie(file, err) = OpenOrRemakeFile(&dir, kFileName);
253   EXPECT_EQ(err, SafeFD::Error::kNoError);
254   EXPECT_TRUE(file.is_valid());
255 }
256 
TEST_F(FileUtilTest,OpenOrRemakeFile_NotInitialized)257 TEST_F(FileUtilTest, OpenOrRemakeFile_NotInitialized) {
258   ASSERT_TRUE(SetupSubdir());
259   SafeFD::Error err;
260   SafeFD dir;
261 
262   SafeFD file;
263   std::tie(file, err) = OpenOrRemakeFile(&dir, kFileName);
264   EXPECT_EQ(err, SafeFD::Error::kNotInitialized);
265   EXPECT_FALSE(file.is_valid());
266 }
267 
TEST_F(FileUtilTest,OpenOrRemakeFile_IOError)268 TEST_F(FileUtilTest, OpenOrRemakeFile_IOError) {
269   ASSERT_TRUE(SetupSubdir());
270   SafeFD::Error err;
271   SafeFD dir;
272 
273   std::tie(dir, err) = root_.OpenExistingDir(sub_dir_path_);
274   EXPECT_EQ(err, SafeFD::Error::kNoError);
275   ASSERT_TRUE(dir.is_valid());
276   ASSERT_EQ(chmod(sub_dir_path_.value().c_str(), 0000), 0);
277 
278   SafeFD file;
279   std::tie(file, err) = OpenOrRemakeFile(&dir, kFileName);
280   EXPECT_EQ(err, SafeFD::Error::kIOError);
281   EXPECT_FALSE(file.is_valid());
282 }
283 
284 }  // namespace brillo
285