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