1 // Copyright 2014 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/file_utils.h"
6
7 #include <sys/stat.h>
8 #include <unistd.h>
9
10 #include <string>
11
12 #include <base/files/file_util.h>
13 #include <base/files/scoped_temp_dir.h>
14 #include <base/rand_util.h>
15 #include <base/strings/string_number_conversions.h>
16 #include <gtest/gtest.h>
17
18 namespace brillo {
19
20 namespace {
21
22 constexpr int kPermissions600 =
23 base::FILE_PERMISSION_READ_BY_USER | base::FILE_PERMISSION_WRITE_BY_USER;
24 constexpr int kPermissions700 = base::FILE_PERMISSION_USER_MASK;
25 constexpr int kPermissions777 = base::FILE_PERMISSION_MASK;
26
GetRandomSuffix()27 std::string GetRandomSuffix() {
28 const int kBufferSize = 6;
29 unsigned char buffer[kBufferSize];
30 base::RandBytes(buffer, arraysize(buffer));
31 return base::HexEncode(buffer, arraysize(buffer));
32 }
33
34 } // namespace
35
36 class FileUtilsTest : public testing::Test {
37 public:
FileUtilsTest()38 FileUtilsTest() {
39 CHECK(temp_dir_.CreateUniqueTempDir());
40 file_path_ = temp_dir_.GetPath().Append("test.temp");
41 }
42
43 protected:
44 base::FilePath file_path_;
45 base::ScopedTempDir temp_dir_;
46
47 // Writes |contents| to |file_path_|. Pulled into a separate function just
48 // to improve readability of tests.
WriteFile(const std::string & contents)49 void WriteFile(const std::string& contents) {
50 EXPECT_EQ(contents.length(),
51 base::WriteFile(file_path_, contents.c_str(), contents.length()));
52 }
53
54 // Verifies that the file at |file_path_| exists and contains |contents|.
ExpectFileContains(const std::string & contents)55 void ExpectFileContains(const std::string& contents) {
56 EXPECT_TRUE(base::PathExists(file_path_));
57 std::string new_contents;
58 EXPECT_TRUE(base::ReadFileToString(file_path_, &new_contents));
59 EXPECT_EQ(contents, new_contents);
60 }
61
62 // Verifies that the file at |file_path_| has |permissions|.
ExpectFilePermissions(int permissions)63 void ExpectFilePermissions(int permissions) {
64 int actual_permissions;
65 EXPECT_TRUE(base::GetPosixFilePermissions(file_path_, &actual_permissions));
66 EXPECT_EQ(permissions, actual_permissions);
67 }
68
69 // Creates a file with a random name in the temporary directory.
GetTempName()70 base::FilePath GetTempName() {
71 return temp_dir_.GetPath().Append(GetRandomSuffix());
72 }
73 };
74
TEST_F(FileUtilsTest,TouchFileCreate)75 TEST_F(FileUtilsTest, TouchFileCreate) {
76 EXPECT_TRUE(TouchFile(file_path_));
77 ExpectFileContains("");
78 ExpectFilePermissions(kPermissions600);
79 }
80
TEST_F(FileUtilsTest,TouchFileCreateThroughUmask)81 TEST_F(FileUtilsTest, TouchFileCreateThroughUmask) {
82 mode_t old_umask = umask(kPermissions777);
83 EXPECT_TRUE(TouchFile(file_path_));
84 umask(old_umask);
85 ExpectFileContains("");
86 ExpectFilePermissions(kPermissions600);
87 }
88
TEST_F(FileUtilsTest,TouchFileCreateDirectoryStructure)89 TEST_F(FileUtilsTest, TouchFileCreateDirectoryStructure) {
90 file_path_ = temp_dir_.GetPath().Append("foo/bar/baz/test.temp");
91 EXPECT_TRUE(TouchFile(file_path_));
92 ExpectFileContains("");
93 }
94
TEST_F(FileUtilsTest,TouchFileExisting)95 TEST_F(FileUtilsTest, TouchFileExisting) {
96 WriteFile("abcd");
97 EXPECT_TRUE(TouchFile(file_path_));
98 ExpectFileContains("abcd");
99 }
100
TEST_F(FileUtilsTest,TouchFileReplaceDirectory)101 TEST_F(FileUtilsTest, TouchFileReplaceDirectory) {
102 EXPECT_TRUE(base::CreateDirectory(file_path_));
103 EXPECT_TRUE(TouchFile(file_path_));
104 EXPECT_FALSE(base::DirectoryExists(file_path_));
105 ExpectFileContains("");
106 }
107
TEST_F(FileUtilsTest,TouchFileReplaceSymlink)108 TEST_F(FileUtilsTest, TouchFileReplaceSymlink) {
109 base::FilePath symlink_target = temp_dir_.GetPath().Append("target.temp");
110 EXPECT_TRUE(base::CreateSymbolicLink(symlink_target, file_path_));
111 EXPECT_TRUE(TouchFile(file_path_));
112 EXPECT_FALSE(base::IsLink(file_path_));
113 ExpectFileContains("");
114 }
115
TEST_F(FileUtilsTest,TouchFileReplaceOtherUser)116 TEST_F(FileUtilsTest, TouchFileReplaceOtherUser) {
117 WriteFile("abcd");
118 EXPECT_TRUE(TouchFile(file_path_, kPermissions777, geteuid() + 1, getegid()));
119 ExpectFileContains("");
120 }
121
TEST_F(FileUtilsTest,TouchFileReplaceOtherGroup)122 TEST_F(FileUtilsTest, TouchFileReplaceOtherGroup) {
123 WriteFile("abcd");
124 EXPECT_TRUE(TouchFile(file_path_, kPermissions777, geteuid(), getegid() + 1));
125 ExpectFileContains("");
126 }
127
TEST_F(FileUtilsTest,TouchFileCreateWithAllPermissions)128 TEST_F(FileUtilsTest, TouchFileCreateWithAllPermissions) {
129 EXPECT_TRUE(TouchFile(file_path_, kPermissions777, geteuid(), getegid()));
130 ExpectFileContains("");
131 ExpectFilePermissions(kPermissions777);
132 }
133
TEST_F(FileUtilsTest,TouchFileCreateWithOwnerPermissions)134 TEST_F(FileUtilsTest, TouchFileCreateWithOwnerPermissions) {
135 EXPECT_TRUE(TouchFile(file_path_, kPermissions700, geteuid(), getegid()));
136 ExpectFileContains("");
137 ExpectFilePermissions(kPermissions700);
138 }
139
TEST_F(FileUtilsTest,TouchFileExistingPermissionsUnchanged)140 TEST_F(FileUtilsTest, TouchFileExistingPermissionsUnchanged) {
141 EXPECT_TRUE(TouchFile(file_path_, kPermissions777, geteuid(), getegid()));
142 EXPECT_TRUE(TouchFile(file_path_, kPermissions700, geteuid(), getegid()));
143 ExpectFileContains("");
144 ExpectFilePermissions(kPermissions777);
145 }
146
TEST_F(FileUtilsTest,WriteFileCanBeReadBack)147 TEST_F(FileUtilsTest, WriteFileCanBeReadBack) {
148 const base::FilePath filename(GetTempName());
149 const std::string content("blablabla");
150 EXPECT_TRUE(WriteStringToFile(filename, content));
151 std::string output;
152 EXPECT_TRUE(ReadFileToString(filename, &output));
153 EXPECT_EQ(content, output);
154 }
155
TEST_F(FileUtilsTest,WriteFileSets0666)156 TEST_F(FileUtilsTest, WriteFileSets0666) {
157 const mode_t mask = 0000;
158 const mode_t mode = 0666;
159 const base::FilePath filename(GetTempName());
160 const std::string content("blablabla");
161 const mode_t old_mask = umask(mask);
162 EXPECT_TRUE(WriteStringToFile(filename, content));
163 int file_mode = 0;
164 EXPECT_TRUE(base::GetPosixFilePermissions(filename, &file_mode));
165 EXPECT_EQ(mode & ~mask, file_mode & 0777);
166 umask(old_mask);
167 }
168
TEST_F(FileUtilsTest,WriteFileCreatesMissingParentDirectoriesWith0700)169 TEST_F(FileUtilsTest, WriteFileCreatesMissingParentDirectoriesWith0700) {
170 const mode_t mask = 0000;
171 const mode_t mode = 0700;
172 const base::FilePath dirname(GetTempName());
173 const base::FilePath subdirname(dirname.Append(GetRandomSuffix()));
174 const base::FilePath filename(subdirname.Append(GetRandomSuffix()));
175 const std::string content("blablabla");
176 EXPECT_TRUE(WriteStringToFile(filename, content));
177 int dir_mode = 0;
178 int subdir_mode = 0;
179 EXPECT_TRUE(base::GetPosixFilePermissions(dirname, &dir_mode));
180 EXPECT_TRUE(base::GetPosixFilePermissions(subdirname, &subdir_mode));
181 EXPECT_EQ(mode & ~mask, dir_mode & 0777);
182 EXPECT_EQ(mode & ~mask, subdir_mode & 0777);
183 const mode_t old_mask = umask(mask);
184 umask(old_mask);
185 }
186
TEST_F(FileUtilsTest,WriteToFileAtomicCanBeReadBack)187 TEST_F(FileUtilsTest, WriteToFileAtomicCanBeReadBack) {
188 const base::FilePath filename(GetTempName());
189 const std::string content("blablabla");
190 EXPECT_TRUE(
191 WriteToFileAtomic(filename, content.data(), content.size(), 0644));
192 std::string output;
193 EXPECT_TRUE(ReadFileToString(filename, &output));
194 EXPECT_EQ(content, output);
195 }
196
TEST_F(FileUtilsTest,WriteToFileAtomicHonorsMode)197 TEST_F(FileUtilsTest, WriteToFileAtomicHonorsMode) {
198 const mode_t mask = 0000;
199 const mode_t mode = 0616;
200 const base::FilePath filename(GetTempName());
201 const std::string content("blablabla");
202 const mode_t old_mask = umask(mask);
203 EXPECT_TRUE(
204 WriteToFileAtomic(filename, content.data(), content.size(), mode));
205 int file_mode = 0;
206 EXPECT_TRUE(base::GetPosixFilePermissions(filename, &file_mode));
207 EXPECT_EQ(mode & ~mask, file_mode & 0777);
208 umask(old_mask);
209 }
210
TEST_F(FileUtilsTest,WriteToFileAtomicHonorsUmask)211 TEST_F(FileUtilsTest, WriteToFileAtomicHonorsUmask) {
212 const mode_t mask = 0073;
213 const mode_t mode = 0777;
214 const base::FilePath filename(GetTempName());
215 const std::string content("blablabla");
216 const mode_t old_mask = umask(mask);
217 EXPECT_TRUE(
218 WriteToFileAtomic(filename, content.data(), content.size(), mode));
219 int file_mode = 0;
220 EXPECT_TRUE(base::GetPosixFilePermissions(filename, &file_mode));
221 EXPECT_EQ(mode & ~mask, file_mode & 0777);
222 umask(old_mask);
223 }
224
TEST_F(FileUtilsTest,WriteToFileAtomicCreatesMissingParentDirectoriesWith0700)225 TEST_F(FileUtilsTest,
226 WriteToFileAtomicCreatesMissingParentDirectoriesWith0700) {
227 const mode_t mask = 0000;
228 const mode_t mode = 0700;
229 const base::FilePath dirname(GetTempName());
230 const base::FilePath subdirname(dirname.Append(GetRandomSuffix()));
231 const base::FilePath filename(subdirname.Append(GetRandomSuffix()));
232 const std::string content("blablabla");
233 EXPECT_TRUE(
234 WriteToFileAtomic(filename, content.data(), content.size(), 0777));
235 int dir_mode = 0;
236 int subdir_mode = 0;
237 EXPECT_TRUE(base::GetPosixFilePermissions(dirname, &dir_mode));
238 EXPECT_TRUE(base::GetPosixFilePermissions(subdirname, &subdir_mode));
239 EXPECT_EQ(mode & ~mask, dir_mode & 0777);
240 EXPECT_EQ(mode & ~mask, subdir_mode & 0777);
241 const mode_t old_mask = umask(mask);
242 umask(old_mask);
243 }
244
245 } // namespace brillo
246