xref: /aosp_15_r20/system/libbase/file_test.cpp (revision 8f0ba417480079999ba552f1087ae592091b9d02)
1  /*
2   * Copyright (C) 2013 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 "android-base/file.h"
18  
19  #include "android-base/utf8.h"
20  
21  #include <gtest/gtest.h>
22  
23  #include <errno.h>
24  #include <fcntl.h>
25  #include <unistd.h>
26  #include <wchar.h>
27  
28  #include <string>
29  
30  #if !defined(_WIN32)
31  #include <pwd.h>
32  #else
33  #include <windows.h>
34  #endif
35  
36  #include "android-base/logging.h"  // and must be after windows.h for ERROR
37  
TEST(file,ReadFileToString_ENOENT)38  TEST(file, ReadFileToString_ENOENT) {
39    std::string s("hello");
40    errno = 0;
41    ASSERT_FALSE(android::base::ReadFileToString("/proc/does-not-exist", &s));
42    EXPECT_EQ(ENOENT, errno);
43    EXPECT_EQ("", s);  // s was cleared.
44  }
45  
TEST(file,ReadFileToString_WriteStringToFile)46  TEST(file, ReadFileToString_WriteStringToFile) {
47    TemporaryFile tf;
48    ASSERT_NE(tf.fd, -1) << tf.path;
49    ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path))
50      << strerror(errno);
51    std::string s;
52    ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
53      << strerror(errno);
54    EXPECT_EQ("abc", s);
55  }
56  
57  // symlinks require elevated privileges on Windows.
58  #if !defined(_WIN32)
TEST(file,ReadFileToString_WriteStringToFile_symlink)59  TEST(file, ReadFileToString_WriteStringToFile_symlink) {
60    TemporaryFile target, link;
61    ASSERT_EQ(0, unlink(link.path));
62    ASSERT_EQ(0, symlink(target.path, link.path));
63    ASSERT_FALSE(android::base::WriteStringToFile("foo", link.path, false));
64    ASSERT_EQ(ELOOP, errno);
65    ASSERT_TRUE(android::base::WriteStringToFile("foo", link.path, true));
66  
67    std::string s;
68    ASSERT_FALSE(android::base::ReadFileToString(link.path, &s));
69    ASSERT_EQ(ELOOP, errno);
70    ASSERT_TRUE(android::base::ReadFileToString(link.path, &s, true));
71    ASSERT_EQ("foo", s);
72  }
73  #endif
74  
75  // WriteStringToFile2 is explicitly for setting Unix permissions, which make no
76  // sense on Windows.
77  #if !defined(_WIN32)
TEST(file,WriteStringToFile2)78  TEST(file, WriteStringToFile2) {
79    TemporaryFile tf;
80    ASSERT_NE(tf.fd, -1) << tf.path;
81    ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path, 0660,
82                                                 getuid(), getgid()))
83        << strerror(errno);
84    struct stat sb;
85    ASSERT_EQ(0, stat(tf.path, &sb));
86    ASSERT_EQ(0660U, static_cast<unsigned int>(sb.st_mode & ~S_IFMT));
87    ASSERT_EQ(getuid(), sb.st_uid);
88    ASSERT_EQ(getgid(), sb.st_gid);
89    std::string s;
90    ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
91      << strerror(errno);
92    EXPECT_EQ("abc", s);
93  }
94  #endif
95  
96  #if defined(_WIN32)
TEST(file,NonUnicodeCharsWindows)97  TEST(file, NonUnicodeCharsWindows) {
98    constexpr auto kMaxEnvVariableValueSize = 32767;
99    std::wstring old_tmp;
100    old_tmp.resize(kMaxEnvVariableValueSize);
101    old_tmp.resize(GetEnvironmentVariableW(L"TMP", old_tmp.data(), old_tmp.size()));
102    if (old_tmp.empty()) {
103      // Can't continue with empty TMP folder.
104      return;
105    }
106  
107    std::wstring new_tmp = old_tmp;
108    if (new_tmp.back() != L'\\') {
109      new_tmp.push_back(L'\\');
110    }
111  
112    {
113      auto path(new_tmp + L"锦绣成都\\");
114      _wmkdir(path.c_str());
115      ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
116  
117      TemporaryFile tf;
118      ASSERT_NE(tf.fd, -1) << tf.path;
119      ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
120  
121      ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
122  
123      std::string s;
124      ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
125      EXPECT_EQ("abc", s);
126    }
127    {
128      auto path(new_tmp + L"директория с длинным именем\\");
129      _wmkdir(path.c_str());
130      ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
131  
132      TemporaryFile tf;
133      ASSERT_NE(tf.fd, -1) << tf.path;
134      ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
135  
136      ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
137  
138      std::string s;
139      ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
140      EXPECT_EQ("abc", s);
141    }
142    {
143      auto path(new_tmp + L"äüöß weiß\\");
144      _wmkdir(path.c_str());
145      ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
146  
147      TemporaryFile tf;
148      ASSERT_NE(tf.fd, -1) << tf.path;
149      ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
150  
151      ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
152  
153      std::string s;
154      ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
155      EXPECT_EQ("abc", s);
156    }
157  
158    SetEnvironmentVariableW(L"TMP", old_tmp.c_str());
159  }
160  
TEST(file,RootDirectoryWindows)161  TEST(file, RootDirectoryWindows) {
162    constexpr auto kMaxEnvVariableValueSize = 32767;
163    std::wstring old_tmp;
164    bool tmp_is_empty = false;
165    old_tmp.resize(kMaxEnvVariableValueSize);
166    old_tmp.resize(GetEnvironmentVariableW(L"TMP", old_tmp.data(), old_tmp.size()));
167    if (old_tmp.empty()) {
168      tmp_is_empty = (GetLastError() == ERROR_ENVVAR_NOT_FOUND);
169    }
170    SetEnvironmentVariableW(L"TMP", L"C:");
171  
172    TemporaryFile tf;
173    ASSERT_NE(tf.fd, -1) << tf.path;
174  
175    SetEnvironmentVariableW(L"TMP", tmp_is_empty ? nullptr : old_tmp.c_str());
176  }
177  #endif
178  
TEST(file,WriteStringToFd_StringLiteral)179  TEST(file, WriteStringToFd_StringLiteral) {
180    TemporaryFile tf;
181    ASSERT_NE(tf.fd, -1) << tf.path;
182    ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
183  
184    ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
185  
186    std::string s;
187    ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
188    EXPECT_EQ("abc", s);
189  }
190  
TEST(file,WriteStringToFd_String)191  TEST(file, WriteStringToFd_String) {
192    std::string testStr = "def";
193    TemporaryFile tf;
194    ASSERT_NE(tf.fd, -1) << tf.path;
195    ASSERT_TRUE(android::base::WriteStringToFd(testStr, tf.fd));
196  
197    ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
198  
199    std::string s;
200    ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
201    EXPECT_EQ(testStr, s);
202  }
203  
TEST(file,WriteStringToFd_StringView)204  TEST(file, WriteStringToFd_StringView) {
205    std::string_view testStrView = "ghi";
206    TemporaryFile tf;
207    ASSERT_NE(tf.fd, -1) << tf.path;
208    ASSERT_TRUE(android::base::WriteStringToFd(testStrView, tf.fd));
209  
210    ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
211  
212    std::string s;
213    ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
214    EXPECT_EQ(testStrView, s);
215  }
216  
TEST(file,WriteFully)217  TEST(file, WriteFully) {
218    TemporaryFile tf;
219    ASSERT_NE(tf.fd, -1) << tf.path;
220    ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
221  
222    ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
223  
224    std::string s;
225    s.resize(3);
226    ASSERT_TRUE(android::base::ReadFully(tf.fd, &s[0], s.size()))
227      << strerror(errno);
228    EXPECT_EQ("abc", s);
229  
230    ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
231  
232    s.resize(1024);
233    ASSERT_FALSE(android::base::ReadFully(tf.fd, &s[0], s.size()));
234  }
235  
TEST(file,RemoveFileIfExists)236  TEST(file, RemoveFileIfExists) {
237    TemporaryFile tf;
238    ASSERT_NE(tf.fd, -1) << tf.path;
239    close(tf.fd);
240    tf.fd = -1;
241    std::string err;
242    ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err)) << err;
243    ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path));
244    TemporaryDir td;
245    ASSERT_FALSE(android::base::RemoveFileIfExists(td.path));
246    ASSERT_FALSE(android::base::RemoveFileIfExists(td.path, &err));
247    ASSERT_EQ("is not a regular file or symbolic link", err);
248  }
249  
TEST(file,RemoveFileIfExists_ENOTDIR)250  TEST(file, RemoveFileIfExists_ENOTDIR) {
251    TemporaryFile tf;
252    close(tf.fd);
253    tf.fd = -1;
254    std::string err{"xxx"};
255    ASSERT_TRUE(android::base::RemoveFileIfExists(std::string{tf.path} + "/abc", &err));
256    ASSERT_EQ("xxx", err);
257  }
258  
259  #if !defined(_WIN32)
TEST(file,RemoveFileIfExists_EACCES)260  TEST(file, RemoveFileIfExists_EACCES) {
261    // EACCES -- one of the directories in the path has no search permission
262    // root can bypass permission restrictions, so drop root.
263    if (getuid() == 0) {
264      passwd* shell = getpwnam("shell");
265      setgid(shell->pw_gid);
266      setuid(shell->pw_uid);
267    }
268  
269    TemporaryDir td;
270    TemporaryFile tf(td.path);
271    close(tf.fd);
272    tf.fd = -1;
273    std::string err{"xxx"};
274    // Remove dir's search permission.
275    ASSERT_TRUE(chmod(td.path, S_IRUSR | S_IWUSR) == 0);
276    ASSERT_FALSE(android::base::RemoveFileIfExists(tf.path, &err));
277    ASSERT_EQ("Permission denied", err);
278    // Set dir's search permission again.
279    ASSERT_TRUE(chmod(td.path, S_IRWXU) == 0);
280    ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err));
281  }
282  #endif
283  
TEST(file,Readlink)284  TEST(file, Readlink) {
285  #if !defined(_WIN32)
286    // Linux doesn't allow empty symbolic links.
287    std::string min("x");
288    // ext2 and ext4 both have PAGE_SIZE limits.
289    // If file encryption is enabled, there's extra overhead to store the
290    // size of the encrypted symlink target. There's also an off-by-one
291    // in current kernels (and marlin/sailfish where we're seeing this
292    // failure are still on 3.18, far from current). http://b/33306057.
293    std::string max(static_cast<size_t>(4096 - 2 - 1 - 1), 'x');
294  
295    TemporaryDir td;
296    std::string min_path{std::string(td.path) + "/" + "min"};
297    std::string max_path{std::string(td.path) + "/" + "max"};
298  
299    ASSERT_EQ(0, symlink(min.c_str(), min_path.c_str()));
300    ASSERT_EQ(0, symlink(max.c_str(), max_path.c_str()));
301  
302    std::string result;
303  
304    result = "wrong";
305    ASSERT_TRUE(android::base::Readlink(min_path, &result));
306    ASSERT_EQ(min, result);
307  
308    result = "wrong";
309    ASSERT_TRUE(android::base::Readlink(max_path, &result));
310    ASSERT_EQ(max, result);
311  #endif
312  }
313  
TEST(file,Realpath)314  TEST(file, Realpath) {
315  #if !defined(_WIN32)
316    TemporaryDir td;
317    std::string basename = android::base::Basename(td.path);
318    std::string dir_name = android::base::Dirname(td.path);
319    std::string base_dir_name = android::base::Basename(dir_name);
320  
321    {
322      std::string path = dir_name + "/../" + base_dir_name + "/" + basename;
323      std::string result;
324      ASSERT_TRUE(android::base::Realpath(path, &result));
325      ASSERT_EQ(td.path, result);
326    }
327  
328    {
329      std::string path = std::string(td.path) + "/..";
330      std::string result;
331      ASSERT_TRUE(android::base::Realpath(path, &result));
332      ASSERT_EQ(dir_name, result);
333    }
334  
335    {
336      errno = 0;
337      std::string path = std::string(td.path) + "/foo.noent";
338      std::string result = "wrong";
339      ASSERT_TRUE(!android::base::Realpath(path, &result));
340      ASSERT_TRUE(result.empty());
341      ASSERT_EQ(ENOENT, errno);
342    }
343  #endif
344  }
345  
TEST(file,GetExecutableDirectory)346  TEST(file, GetExecutableDirectory) {
347    std::string path = android::base::GetExecutableDirectory();
348    ASSERT_NE("", path);
349    ASSERT_NE(android::base::GetExecutablePath(), path);
350    ASSERT_EQ('/', path[0]);
351    ASSERT_NE('/', path[path.size() - 1]);
352  }
353  
TEST(file,GetExecutablePath)354  TEST(file, GetExecutablePath) {
355    ASSERT_NE("", android::base::GetExecutablePath());
356  }
357  
TEST(file,Basename)358  TEST(file, Basename) {
359    EXPECT_EQ("sh", android::base::Basename("/system/bin/sh"));
360    EXPECT_EQ("sh", android::base::Basename("sh"));
361    EXPECT_EQ("sh", android::base::Basename("/system/bin/sh/"));
362  
363    // Since we've copy & pasted bionic's implementation, copy & paste the tests.
364    EXPECT_EQ(".",   android::base::Basename(""));
365    EXPECT_EQ("lib", android::base::Basename("/usr/lib"));
366    EXPECT_EQ("usr", android::base::Basename("/usr/"));
367    EXPECT_EQ("usr", android::base::Basename("usr"));
368    EXPECT_EQ("/",   android::base::Basename("/"));
369    EXPECT_EQ(".",   android::base::Basename("."));
370    EXPECT_EQ("..",  android::base::Basename(".."));
371    EXPECT_EQ("/",   android::base::Basename("///"));
372    EXPECT_EQ("lib", android::base::Basename("//usr//lib//"));
373  }
374  
TEST(file,Dirname)375  TEST(file, Dirname) {
376    EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh"));
377    EXPECT_EQ(".", android::base::Dirname("sh"));
378    EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh/"));
379  
380    // Since we've copy & pasted bionic's implementation, copy & paste the tests.
381    EXPECT_EQ(".", android::base::Dirname(""));
382    EXPECT_EQ("/usr", android::base::Dirname("/usr/lib"));
383    EXPECT_EQ("/", android::base::Dirname("/usr/"));
384    EXPECT_EQ(".", android::base::Dirname("usr"));
385    EXPECT_EQ(".", android::base::Dirname("."));
386    EXPECT_EQ(".", android::base::Dirname(".."));
387    EXPECT_EQ("/", android::base::Dirname("/"));
388  }
389  
TEST(file,ReadFileToString_capacity)390  TEST(file, ReadFileToString_capacity) {
391    TemporaryFile tf;
392    ASSERT_NE(tf.fd, -1) << tf.path;
393  
394    // For a huge file, the overhead should still be small.
395    std::string s;
396    size_t size = 16 * 1024 * 1024;
397    ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
398    ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
399    EXPECT_EQ(size, s.size());
400    EXPECT_LT(s.capacity(), size + 16);
401  
402    // Even for weird badly-aligned sizes.
403    size += 12345;
404    ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
405    ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
406    EXPECT_EQ(size, s.size());
407    EXPECT_LT(s.capacity(), size + 16);
408  
409    // We'll shrink an enormous string if you read a small file into it.
410    size = 64;
411    ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
412    ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
413    EXPECT_EQ(size, s.size());
414    EXPECT_LT(s.capacity(), size + 16);
415  }
416  
TEST(file,ReadFileToString_capacity_0)417  TEST(file, ReadFileToString_capacity_0) {
418    TemporaryFile tf;
419    ASSERT_NE(tf.fd, -1) << tf.path;
420  
421    // Because /proc reports its files as zero-length, we don't actually trust
422    // any file that claims to be zero-length. Rather than add increasingly
423    // complex heuristics for shrinking the passed-in string in that case, we
424    // currently leave it alone.
425    std::string s;
426    size_t initial_capacity = s.capacity();
427    ASSERT_TRUE(android::base::WriteStringToFile("", tf.path));
428    ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
429    EXPECT_EQ(0U, s.size());
430    EXPECT_EQ(initial_capacity, s.capacity());
431  }
432