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