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