1 // Copyright (c) 2012 The Chromium 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 "base/files/file_util.h" 6 7 #include <dirent.h> 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <libgen.h> 11 #include <limits.h> 12 #include <stddef.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <sys/errno.h> 17 #include <sys/mman.h> 18 #include <sys/param.h> 19 #include <sys/stat.h> 20 #include <sys/time.h> 21 #include <sys/types.h> 22 #include <time.h> 23 #include <unistd.h> 24 25 #include "base/environment.h" 26 #include "base/files/file_enumerator.h" 27 #include "base/files/file_path.h" 28 #include "base/files/scoped_file.h" 29 #include "base/logging.h" 30 #include "base/macros.h" 31 #include "base/memory/singleton.h" 32 #include "base/posix/eintr_wrapper.h" 33 #include "base/stl_util.h" 34 #include "base/strings/string_split.h" 35 #include "base/strings/string_util.h" 36 #include "base/strings/stringprintf.h" 37 #include "base/strings/sys_string_conversions.h" 38 #include "base/strings/utf_string_conversions.h" 39 #include "base/sys_info.h" 40 #include "base/threading/thread_restrictions.h" 41 #include "base/time/time.h" 42 #include "build/build_config.h" 43 44 #if defined(OS_MACOSX) 45 #include <AvailabilityMacros.h> 46 #include "base/mac/foundation_util.h" 47 #endif 48 49 #if defined(OS_ANDROID) 50 #include "base/android/content_uri_utils.h" 51 #include "base/os_compat_android.h" 52 #include "base/path_service.h" 53 #endif 54 55 #if !defined(OS_IOS) 56 #include <grp.h> 57 #endif 58 59 namespace base { 60 61 namespace { 62 63 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) 64 static int CallStat(const char *path, stat_wrapper_t *sb) { 65 ThreadRestrictions::AssertIOAllowed(); 66 return stat(path, sb); 67 } 68 static int CallLstat(const char *path, stat_wrapper_t *sb) { 69 ThreadRestrictions::AssertIOAllowed(); 70 return lstat(path, sb); 71 } 72 #else // defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) 73 static int CallStat(const char *path, stat_wrapper_t *sb) { 74 ThreadRestrictions::AssertIOAllowed(); 75 return stat64(path, sb); 76 } 77 static int CallLstat(const char *path, stat_wrapper_t *sb) { 78 ThreadRestrictions::AssertIOAllowed(); 79 return lstat64(path, sb); 80 } 81 #endif // !(defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)) 82 83 #if !defined(OS_NACL_NONSFI) 84 // Helper for NormalizeFilePath(), defined below. 85 bool RealPath(const FilePath& path, FilePath* real_path) { 86 ThreadRestrictions::AssertIOAllowed(); // For realpath(). 87 FilePath::CharType buf[PATH_MAX]; 88 if (!realpath(path.value().c_str(), buf)) 89 return false; 90 91 *real_path = FilePath(buf); 92 return true; 93 } 94 95 // Helper for VerifyPathControlledByUser. 96 bool VerifySpecificPathControlledByUser(const FilePath& path, 97 uid_t owner_uid, 98 const std::set<gid_t>& group_gids) { 99 stat_wrapper_t stat_info; 100 if (CallLstat(path.value().c_str(), &stat_info) != 0) { 101 DPLOG(ERROR) << "Failed to get information on path " 102 << path.value(); 103 return false; 104 } 105 106 if (S_ISLNK(stat_info.st_mode)) { 107 DLOG(ERROR) << "Path " << path.value() 108 << " is a symbolic link."; 109 return false; 110 } 111 112 if (stat_info.st_uid != owner_uid) { 113 DLOG(ERROR) << "Path " << path.value() 114 << " is owned by the wrong user."; 115 return false; 116 } 117 118 if ((stat_info.st_mode & S_IWGRP) && 119 !ContainsKey(group_gids, stat_info.st_gid)) { 120 DLOG(ERROR) << "Path " << path.value() 121 << " is writable by an unprivileged group."; 122 return false; 123 } 124 125 if (stat_info.st_mode & S_IWOTH) { 126 DLOG(ERROR) << "Path " << path.value() 127 << " is writable by any user."; 128 return false; 129 } 130 131 return true; 132 } 133 134 std::string TempFileName() { 135 #if defined(OS_MACOSX) 136 return StringPrintf(".%s.XXXXXX", base::mac::BaseBundleID()); 137 #endif 138 139 #if defined(GOOGLE_CHROME_BUILD) 140 return std::string(".com.google.Chrome.XXXXXX"); 141 #else 142 return std::string(".org.chromium.Chromium.XXXXXX"); 143 #endif 144 } 145 146 // Creates and opens a temporary file in |directory|, returning the 147 // file descriptor. |path| is set to the temporary file path. 148 // This function does NOT unlink() the file. 149 int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) { 150 ThreadRestrictions::AssertIOAllowed(); // For call to mkstemp(). 151 *path = directory.Append(base::TempFileName()); 152 const std::string& tmpdir_string = path->value(); 153 // this should be OK since mkstemp just replaces characters in place 154 char* buffer = const_cast<char*>(tmpdir_string.c_str()); 155 156 return HANDLE_EINTR(mkstemp(buffer)); 157 } 158 159 #if defined(OS_LINUX) 160 // Determine if /dev/shm files can be mapped and then mprotect'd PROT_EXEC. 161 // This depends on the mount options used for /dev/shm, which vary among 162 // different Linux distributions and possibly local configuration. It also 163 // depends on details of kernel--ChromeOS uses the noexec option for /dev/shm 164 // but its kernel allows mprotect with PROT_EXEC anyway. 165 bool DetermineDevShmExecutable() { 166 bool result = false; 167 FilePath path; 168 169 ScopedFD fd(CreateAndOpenFdForTemporaryFile(FilePath("/dev/shm"), &path)); 170 if (fd.is_valid()) { 171 DeleteFile(path, false); 172 long sysconf_result = sysconf(_SC_PAGESIZE); 173 CHECK_GE(sysconf_result, 0); 174 size_t pagesize = static_cast<size_t>(sysconf_result); 175 CHECK_GE(sizeof(pagesize), sizeof(sysconf_result)); 176 void* mapping = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd.get(), 0); 177 if (mapping != MAP_FAILED) { 178 if (mprotect(mapping, pagesize, PROT_READ | PROT_EXEC) == 0) 179 result = true; 180 munmap(mapping, pagesize); 181 } 182 } 183 return result; 184 } 185 #endif // defined(OS_LINUX) 186 #endif // !defined(OS_NACL_NONSFI) 187 188 #if !defined(OS_MACOSX) 189 // Appends |mode_char| to |mode| before the optional character set encoding; see 190 // https://www.gnu.org/software/libc/manual/html_node/Opening-Streams.html for 191 // details. 192 std::string AppendModeCharacter(StringPiece mode, char mode_char) { 193 std::string result(mode.as_string()); 194 size_t comma_pos = result.find(','); 195 result.insert(comma_pos == std::string::npos ? result.length() : comma_pos, 1, 196 mode_char); 197 return result; 198 } 199 #endif 200 201 } // namespace 202 203 #if !defined(OS_NACL_NONSFI) 204 FilePath MakeAbsoluteFilePath(const FilePath& input) { 205 ThreadRestrictions::AssertIOAllowed(); 206 char full_path[PATH_MAX]; 207 if (realpath(input.value().c_str(), full_path) == NULL) 208 return FilePath(); 209 return FilePath(full_path); 210 } 211 212 // TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*" 213 // which works both with and without the recursive flag. I'm not sure we need 214 // that functionality. If not, remove from file_util_win.cc, otherwise add it 215 // here. 216 bool DeleteFile(const FilePath& path, bool recursive) { 217 ThreadRestrictions::AssertIOAllowed(); 218 const char* path_str = path.value().c_str(); 219 stat_wrapper_t file_info; 220 int test = CallLstat(path_str, &file_info); 221 if (test != 0) { 222 // The Windows version defines this condition as success. 223 bool ret = (errno == ENOENT || errno == ENOTDIR); 224 return ret; 225 } 226 if (!S_ISDIR(file_info.st_mode)) 227 return (unlink(path_str) == 0); 228 if (!recursive) 229 return (rmdir(path_str) == 0); 230 231 bool success = true; 232 std::stack<std::string> directories; 233 directories.push(path.value()); 234 FileEnumerator traversal(path, true, 235 FileEnumerator::FILES | FileEnumerator::DIRECTORIES | 236 FileEnumerator::SHOW_SYM_LINKS); 237 for (FilePath current = traversal.Next(); success && !current.empty(); 238 current = traversal.Next()) { 239 if (traversal.GetInfo().IsDirectory()) 240 directories.push(current.value()); 241 else 242 success = (unlink(current.value().c_str()) == 0); 243 } 244 245 while (success && !directories.empty()) { 246 FilePath dir = FilePath(directories.top()); 247 directories.pop(); 248 success = (rmdir(dir.value().c_str()) == 0); 249 } 250 return success; 251 } 252 253 bool ReplaceFile(const FilePath& from_path, 254 const FilePath& to_path, 255 File::Error* error) { 256 ThreadRestrictions::AssertIOAllowed(); 257 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) 258 return true; 259 if (error) 260 *error = File::OSErrorToFileError(errno); 261 return false; 262 } 263 264 bool CopyDirectory(const FilePath& from_path, 265 const FilePath& to_path, 266 bool recursive) { 267 ThreadRestrictions::AssertIOAllowed(); 268 // Some old callers of CopyDirectory want it to support wildcards. 269 // After some discussion, we decided to fix those callers. 270 // Break loudly here if anyone tries to do this. 271 DCHECK(to_path.value().find('*') == std::string::npos); 272 DCHECK(from_path.value().find('*') == std::string::npos); 273 274 if (from_path.value().size() >= PATH_MAX) { 275 return false; 276 } 277 278 // This function does not properly handle destinations within the source 279 FilePath real_to_path = to_path; 280 if (PathExists(real_to_path)) { 281 real_to_path = MakeAbsoluteFilePath(real_to_path); 282 if (real_to_path.empty()) 283 return false; 284 } else { 285 real_to_path = MakeAbsoluteFilePath(real_to_path.DirName()); 286 if (real_to_path.empty()) 287 return false; 288 } 289 FilePath real_from_path = MakeAbsoluteFilePath(from_path); 290 if (real_from_path.empty()) 291 return false; 292 if (real_to_path == real_from_path || real_from_path.IsParent(real_to_path)) 293 return false; 294 295 int traverse_type = FileEnumerator::FILES | FileEnumerator::SHOW_SYM_LINKS; 296 if (recursive) 297 traverse_type |= FileEnumerator::DIRECTORIES; 298 FileEnumerator traversal(from_path, recursive, traverse_type); 299 300 // We have to mimic windows behavior here. |to_path| may not exist yet, 301 // start the loop with |to_path|. 302 struct stat from_stat; 303 FilePath current = from_path; 304 if (stat(from_path.value().c_str(), &from_stat) < 0) { 305 DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: " 306 << from_path.value() << " errno = " << errno; 307 return false; 308 } 309 struct stat to_path_stat; 310 FilePath from_path_base = from_path; 311 if (recursive && stat(to_path.value().c_str(), &to_path_stat) == 0 && 312 S_ISDIR(to_path_stat.st_mode)) { 313 // If the destination already exists and is a directory, then the 314 // top level of source needs to be copied. 315 from_path_base = from_path.DirName(); 316 } 317 318 // The Windows version of this function assumes that non-recursive calls 319 // will always have a directory for from_path. 320 // TODO(maruel): This is not necessary anymore. 321 DCHECK(recursive || S_ISDIR(from_stat.st_mode)); 322 323 bool success = true; 324 while (success && !current.empty()) { 325 // current is the source path, including from_path, so append 326 // the suffix after from_path to to_path to create the target_path. 327 FilePath target_path(to_path); 328 if (from_path_base != current) { 329 if (!from_path_base.AppendRelativePath(current, &target_path)) { 330 success = false; 331 break; 332 } 333 } 334 335 if (S_ISDIR(from_stat.st_mode)) { 336 if (mkdir(target_path.value().c_str(), 337 (from_stat.st_mode & 01777) | S_IRUSR | S_IXUSR | S_IWUSR) != 338 0 && 339 errno != EEXIST) { 340 DLOG(ERROR) << "CopyDirectory() couldn't create directory: " 341 << target_path.value() << " errno = " << errno; 342 success = false; 343 } 344 } else if (S_ISREG(from_stat.st_mode)) { 345 if (!CopyFile(current, target_path)) { 346 DLOG(ERROR) << "CopyDirectory() couldn't create file: " 347 << target_path.value(); 348 success = false; 349 } 350 } else { 351 DLOG(WARNING) << "CopyDirectory() skipping non-regular file: " 352 << current.value(); 353 } 354 355 current = traversal.Next(); 356 if (!current.empty()) 357 from_stat = traversal.GetInfo().stat(); 358 } 359 360 return success; 361 } 362 #endif // !defined(OS_NACL_NONSFI) 363 364 bool CreateLocalNonBlockingPipe(int fds[2]) { 365 #if defined(OS_LINUX) 366 return pipe2(fds, O_CLOEXEC | O_NONBLOCK) == 0; 367 #else 368 int raw_fds[2]; 369 if (pipe(raw_fds) != 0) 370 return false; 371 ScopedFD fd_out(raw_fds[0]); 372 ScopedFD fd_in(raw_fds[1]); 373 if (!SetCloseOnExec(fd_out.get())) 374 return false; 375 if (!SetCloseOnExec(fd_in.get())) 376 return false; 377 if (!SetNonBlocking(fd_out.get())) 378 return false; 379 if (!SetNonBlocking(fd_in.get())) 380 return false; 381 fds[0] = fd_out.release(); 382 fds[1] = fd_in.release(); 383 return true; 384 #endif 385 } 386 387 bool SetNonBlocking(int fd) { 388 const int flags = fcntl(fd, F_GETFL); 389 if (flags == -1) 390 return false; 391 if (flags & O_NONBLOCK) 392 return true; 393 if (HANDLE_EINTR(fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1) 394 return false; 395 return true; 396 } 397 398 bool SetCloseOnExec(int fd) { 399 #if defined(OS_NACL_NONSFI) 400 const int flags = 0; 401 #else 402 const int flags = fcntl(fd, F_GETFD); 403 if (flags == -1) 404 return false; 405 if (flags & FD_CLOEXEC) 406 return true; 407 #endif // defined(OS_NACL_NONSFI) 408 if (HANDLE_EINTR(fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1) 409 return false; 410 return true; 411 } 412 413 bool PathExists(const FilePath& path) { 414 ThreadRestrictions::AssertIOAllowed(); 415 #if defined(OS_ANDROID) 416 if (path.IsContentUri()) { 417 return ContentUriExists(path); 418 } 419 #endif 420 return access(path.value().c_str(), F_OK) == 0; 421 } 422 423 #if !defined(OS_NACL_NONSFI) 424 bool PathIsWritable(const FilePath& path) { 425 ThreadRestrictions::AssertIOAllowed(); 426 return access(path.value().c_str(), W_OK) == 0; 427 } 428 #endif // !defined(OS_NACL_NONSFI) 429 430 bool DirectoryExists(const FilePath& path) { 431 ThreadRestrictions::AssertIOAllowed(); 432 stat_wrapper_t file_info; 433 if (CallStat(path.value().c_str(), &file_info) == 0) 434 return S_ISDIR(file_info.st_mode); 435 return false; 436 } 437 438 bool ReadFromFD(int fd, char* buffer, size_t bytes) { 439 size_t total_read = 0; 440 while (total_read < bytes) { 441 ssize_t bytes_read = 442 HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read)); 443 if (bytes_read <= 0) 444 break; 445 total_read += bytes_read; 446 } 447 return total_read == bytes; 448 } 449 450 #if !defined(OS_NACL_NONSFI) 451 bool CreateSymbolicLink(const FilePath& target_path, 452 const FilePath& symlink_path) { 453 DCHECK(!symlink_path.empty()); 454 DCHECK(!target_path.empty()); 455 return ::symlink(target_path.value().c_str(), 456 symlink_path.value().c_str()) != -1; 457 } 458 459 bool ReadSymbolicLink(const FilePath& symlink_path, FilePath* target_path) { 460 DCHECK(!symlink_path.empty()); 461 DCHECK(target_path); 462 char buf[PATH_MAX]; 463 ssize_t count = ::readlink(symlink_path.value().c_str(), buf, arraysize(buf)); 464 465 if (count <= 0) { 466 target_path->clear(); 467 return false; 468 } 469 470 *target_path = FilePath(FilePath::StringType(buf, count)); 471 return true; 472 } 473 474 bool GetPosixFilePermissions(const FilePath& path, int* mode) { 475 ThreadRestrictions::AssertIOAllowed(); 476 DCHECK(mode); 477 478 stat_wrapper_t file_info; 479 // Uses stat(), because on symbolic link, lstat() does not return valid 480 // permission bits in st_mode 481 if (CallStat(path.value().c_str(), &file_info) != 0) 482 return false; 483 484 *mode = file_info.st_mode & FILE_PERMISSION_MASK; 485 return true; 486 } 487 488 bool SetPosixFilePermissions(const FilePath& path, 489 int mode) { 490 ThreadRestrictions::AssertIOAllowed(); 491 DCHECK_EQ(mode & ~FILE_PERMISSION_MASK, 0); 492 493 // Calls stat() so that we can preserve the higher bits like S_ISGID. 494 stat_wrapper_t stat_buf; 495 if (CallStat(path.value().c_str(), &stat_buf) != 0) 496 return false; 497 498 // Clears the existing permission bits, and adds the new ones. 499 mode_t updated_mode_bits = stat_buf.st_mode & ~FILE_PERMISSION_MASK; 500 updated_mode_bits |= mode & FILE_PERMISSION_MASK; 501 502 if (HANDLE_EINTR(chmod(path.value().c_str(), updated_mode_bits)) != 0) 503 return false; 504 505 return true; 506 } 507 508 bool ExecutableExistsInPath(Environment* env, 509 const FilePath::StringType& executable) { 510 std::string path; 511 if (!env->GetVar("PATH", &path)) { 512 LOG(ERROR) << "No $PATH variable. Assuming no " << executable << "."; 513 return false; 514 } 515 516 for (const StringPiece& cur_path : 517 SplitStringPiece(path, ":", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) { 518 FilePath file(cur_path); 519 int permissions; 520 if (GetPosixFilePermissions(file.Append(executable), &permissions) && 521 (permissions & FILE_PERMISSION_EXECUTE_BY_USER)) 522 return true; 523 } 524 return false; 525 } 526 527 #if !defined(OS_MACOSX) 528 // This is implemented in file_util_mac.mm for Mac. 529 bool GetTempDir(FilePath* path) { 530 const char* tmp = getenv("TMPDIR"); 531 if (tmp) { 532 *path = FilePath(tmp); 533 } else { 534 #if defined(OS_ANDROID) 535 return PathService::Get(base::DIR_CACHE, path); 536 #elif defined(__ANDROID__) 537 *path = FilePath("/data/local/tmp"); 538 #else 539 *path = FilePath("/tmp"); 540 #endif 541 } 542 return true; 543 } 544 #endif // !defined(OS_MACOSX) 545 546 #if !defined(OS_MACOSX) // Mac implementation is in file_util_mac.mm. 547 FilePath GetHomeDir() { 548 #if defined(OS_CHROMEOS) 549 if (SysInfo::IsRunningOnChromeOS()) { 550 // On Chrome OS chrome::DIR_USER_DATA is overridden with a primary user 551 // homedir once it becomes available. Return / as the safe option. 552 return FilePath("/"); 553 } 554 #endif 555 556 const char* home_dir = getenv("HOME"); 557 if (home_dir && home_dir[0]) 558 return FilePath(home_dir); 559 560 #if defined(OS_ANDROID) 561 DLOG(WARNING) << "OS_ANDROID: Home directory lookup not yet implemented."; 562 #endif 563 564 FilePath rv; 565 if (GetTempDir(&rv)) 566 return rv; 567 568 // Last resort. 569 return FilePath("/tmp"); 570 } 571 #endif // !defined(OS_MACOSX) 572 573 bool CreateTemporaryFile(FilePath* path) { 574 ThreadRestrictions::AssertIOAllowed(); // For call to close(). 575 FilePath directory; 576 if (!GetTempDir(&directory)) 577 return false; 578 int fd = CreateAndOpenFdForTemporaryFile(directory, path); 579 if (fd < 0) 580 return false; 581 close(fd); 582 return true; 583 } 584 585 FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { 586 int fd = CreateAndOpenFdForTemporaryFile(dir, path); 587 if (fd < 0) 588 return NULL; 589 590 FILE* file = fdopen(fd, "a+"); 591 if (!file) 592 close(fd); 593 return file; 594 } 595 596 bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) { 597 ThreadRestrictions::AssertIOAllowed(); // For call to close(). 598 int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file); 599 return ((fd >= 0) && !IGNORE_EINTR(close(fd))); 600 } 601 602 static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir, 603 const FilePath::StringType& name_tmpl, 604 FilePath* new_dir) { 605 ThreadRestrictions::AssertIOAllowed(); // For call to mkdtemp(). 606 DCHECK(name_tmpl.find("XXXXXX") != FilePath::StringType::npos) 607 << "Directory name template must contain \"XXXXXX\"."; 608 609 FilePath sub_dir = base_dir.Append(name_tmpl); 610 std::string sub_dir_string = sub_dir.value(); 611 612 // this should be OK since mkdtemp just replaces characters in place 613 char* buffer = const_cast<char*>(sub_dir_string.c_str()); 614 char* dtemp = mkdtemp(buffer); 615 if (!dtemp) { 616 DPLOG(ERROR) << "mkdtemp"; 617 return false; 618 } 619 *new_dir = FilePath(dtemp); 620 return true; 621 } 622 623 bool CreateTemporaryDirInDir(const FilePath& base_dir, 624 const FilePath::StringType& prefix, 625 FilePath* new_dir) { 626 FilePath::StringType mkdtemp_template = prefix; 627 mkdtemp_template.append(FILE_PATH_LITERAL("XXXXXX")); 628 return CreateTemporaryDirInDirImpl(base_dir, mkdtemp_template, new_dir); 629 } 630 631 bool CreateNewTempDirectory(const FilePath::StringType& prefix, 632 FilePath* new_temp_path) { 633 FilePath tmpdir; 634 if (!GetTempDir(&tmpdir)) 635 return false; 636 637 return CreateTemporaryDirInDirImpl(tmpdir, TempFileName(), new_temp_path); 638 } 639 640 bool CreateDirectoryAndGetError(const FilePath& full_path, 641 File::Error* error) { 642 ThreadRestrictions::AssertIOAllowed(); // For call to mkdir(). 643 std::vector<FilePath> subpaths; 644 645 // Collect a list of all parent directories. 646 FilePath last_path = full_path; 647 subpaths.push_back(full_path); 648 for (FilePath path = full_path.DirName(); 649 path.value() != last_path.value(); path = path.DirName()) { 650 subpaths.push_back(path); 651 last_path = path; 652 } 653 654 // Iterate through the parents and create the missing ones. 655 for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin(); 656 i != subpaths.rend(); ++i) { 657 if (DirectoryExists(*i)) 658 continue; 659 if (mkdir(i->value().c_str(), 0700) == 0) 660 continue; 661 // Mkdir failed, but it might have failed with EEXIST, or some other error 662 // due to the the directory appearing out of thin air. This can occur if 663 // two processes are trying to create the same file system tree at the same 664 // time. Check to see if it exists and make sure it is a directory. 665 int saved_errno = errno; 666 if (!DirectoryExists(*i)) { 667 if (error) 668 *error = File::OSErrorToFileError(saved_errno); 669 return false; 670 } 671 } 672 return true; 673 } 674 675 bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) { 676 FilePath real_path_result; 677 if (!RealPath(path, &real_path_result)) 678 return false; 679 680 // To be consistant with windows, fail if |real_path_result| is a 681 // directory. 682 stat_wrapper_t file_info; 683 if (CallStat(real_path_result.value().c_str(), &file_info) != 0 || 684 S_ISDIR(file_info.st_mode)) 685 return false; 686 687 *normalized_path = real_path_result; 688 return true; 689 } 690 691 // TODO(rkc): Refactor GetFileInfo and FileEnumerator to handle symlinks 692 // correctly. http://code.google.com/p/chromium-os/issues/detail?id=15948 693 bool IsLink(const FilePath& file_path) { 694 stat_wrapper_t st; 695 // If we can't lstat the file, it's safe to assume that the file won't at 696 // least be a 'followable' link. 697 if (CallLstat(file_path.value().c_str(), &st) != 0) 698 return false; 699 700 if (S_ISLNK(st.st_mode)) 701 return true; 702 else 703 return false; 704 } 705 706 bool GetFileInfo(const FilePath& file_path, File::Info* results) { 707 stat_wrapper_t file_info; 708 #if defined(OS_ANDROID) 709 if (file_path.IsContentUri()) { 710 File file = OpenContentUriForRead(file_path); 711 if (!file.IsValid()) 712 return false; 713 return file.GetInfo(results); 714 } else { 715 #endif // defined(OS_ANDROID) 716 if (CallStat(file_path.value().c_str(), &file_info) != 0) 717 return false; 718 #if defined(OS_ANDROID) 719 } 720 #endif // defined(OS_ANDROID) 721 722 results->FromStat(file_info); 723 return true; 724 } 725 #endif // !defined(OS_NACL_NONSFI) 726 727 FILE* OpenFile(const FilePath& filename, const char* mode) { 728 // 'e' is unconditionally added below, so be sure there is not one already 729 // present before a comma in |mode|. 730 DCHECK( 731 strchr(mode, 'e') == nullptr || 732 (strchr(mode, ',') != nullptr && strchr(mode, 'e') > strchr(mode, ','))); 733 ThreadRestrictions::AssertIOAllowed(); 734 FILE* result = NULL; 735 #if defined(OS_MACOSX) 736 // macOS does not provide a mode character to set O_CLOEXEC; see 737 // https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/fopen.3.html. 738 const char* the_mode = mode; 739 #else 740 std::string mode_with_e(AppendModeCharacter(mode, 'e')); 741 const char* the_mode = mode_with_e.c_str(); 742 #endif 743 do { 744 result = fopen(filename.value().c_str(), the_mode); 745 } while (!result && errno == EINTR); 746 #if defined(OS_MACOSX) 747 // Mark the descriptor as close-on-exec. 748 if (result) 749 SetCloseOnExec(fileno(result)); 750 #endif 751 return result; 752 } 753 754 // NaCl doesn't implement system calls to open files directly. 755 #if !defined(OS_NACL) 756 FILE* FileToFILE(File file, const char* mode) { 757 FILE* stream = fdopen(file.GetPlatformFile(), mode); 758 if (stream) 759 file.TakePlatformFile(); 760 return stream; 761 } 762 #endif // !defined(OS_NACL) 763 764 int ReadFile(const FilePath& filename, char* data, int max_size) { 765 ThreadRestrictions::AssertIOAllowed(); 766 int fd = HANDLE_EINTR(open(filename.value().c_str(), O_RDONLY)); 767 if (fd < 0) 768 return -1; 769 770 ssize_t bytes_read = HANDLE_EINTR(read(fd, data, max_size)); 771 if (IGNORE_EINTR(close(fd)) < 0) 772 return -1; 773 return bytes_read; 774 } 775 776 int WriteFile(const FilePath& filename, const char* data, int size) { 777 ThreadRestrictions::AssertIOAllowed(); 778 int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666)); 779 if (fd < 0) 780 return -1; 781 782 int bytes_written = WriteFileDescriptor(fd, data, size) ? size : -1; 783 if (IGNORE_EINTR(close(fd)) < 0) 784 return -1; 785 return bytes_written; 786 } 787 788 bool WriteFileDescriptor(const int fd, const char* data, int size) { 789 // Allow for partial writes. 790 ssize_t bytes_written_total = 0; 791 for (ssize_t bytes_written_partial = 0; bytes_written_total < size; 792 bytes_written_total += bytes_written_partial) { 793 bytes_written_partial = 794 HANDLE_EINTR(write(fd, data + bytes_written_total, 795 size - bytes_written_total)); 796 if (bytes_written_partial < 0) 797 return false; 798 } 799 800 return true; 801 } 802 803 #if !defined(OS_NACL_NONSFI) 804 805 bool AppendToFile(const FilePath& filename, const char* data, int size) { 806 ThreadRestrictions::AssertIOAllowed(); 807 bool ret = true; 808 int fd = HANDLE_EINTR(open(filename.value().c_str(), O_WRONLY | O_APPEND)); 809 if (fd < 0) { 810 VPLOG(1) << "Unable to create file " << filename.value(); 811 return false; 812 } 813 814 // This call will either write all of the data or return false. 815 if (!WriteFileDescriptor(fd, data, size)) { 816 VPLOG(1) << "Error while writing to file " << filename.value(); 817 ret = false; 818 } 819 820 if (IGNORE_EINTR(close(fd)) < 0) { 821 VPLOG(1) << "Error while closing file " << filename.value(); 822 return false; 823 } 824 825 return ret; 826 } 827 828 // Gets the current working directory for the process. 829 bool GetCurrentDirectory(FilePath* dir) { 830 // getcwd can return ENOENT, which implies it checks against the disk. 831 ThreadRestrictions::AssertIOAllowed(); 832 833 char system_buffer[PATH_MAX] = ""; 834 if (!getcwd(system_buffer, sizeof(system_buffer))) { 835 NOTREACHED(); 836 return false; 837 } 838 *dir = FilePath(system_buffer); 839 return true; 840 } 841 842 // Sets the current working directory for the process. 843 bool SetCurrentDirectory(const FilePath& path) { 844 ThreadRestrictions::AssertIOAllowed(); 845 int ret = chdir(path.value().c_str()); 846 return !ret; 847 } 848 849 bool VerifyPathControlledByUser(const FilePath& base, 850 const FilePath& path, 851 uid_t owner_uid, 852 const std::set<gid_t>& group_gids) { 853 if (base != path && !base.IsParent(path)) { 854 DLOG(ERROR) << "|base| must be a subdirectory of |path|. base = \"" 855 << base.value() << "\", path = \"" << path.value() << "\""; 856 return false; 857 } 858 859 std::vector<FilePath::StringType> base_components; 860 std::vector<FilePath::StringType> path_components; 861 862 base.GetComponents(&base_components); 863 path.GetComponents(&path_components); 864 865 std::vector<FilePath::StringType>::const_iterator ib, ip; 866 for (ib = base_components.begin(), ip = path_components.begin(); 867 ib != base_components.end(); ++ib, ++ip) { 868 // |base| must be a subpath of |path|, so all components should match. 869 // If these CHECKs fail, look at the test that base is a parent of 870 // path at the top of this function. 871 DCHECK(ip != path_components.end()); 872 DCHECK(*ip == *ib); 873 } 874 875 FilePath current_path = base; 876 if (!VerifySpecificPathControlledByUser(current_path, owner_uid, group_gids)) 877 return false; 878 879 for (; ip != path_components.end(); ++ip) { 880 current_path = current_path.Append(*ip); 881 if (!VerifySpecificPathControlledByUser( 882 current_path, owner_uid, group_gids)) 883 return false; 884 } 885 return true; 886 } 887 888 #if defined(OS_MACOSX) && !defined(OS_IOS) 889 bool VerifyPathControlledByAdmin(const FilePath& path) { 890 const unsigned kRootUid = 0; 891 const FilePath kFileSystemRoot("/"); 892 893 // The name of the administrator group on mac os. 894 const char* const kAdminGroupNames[] = { 895 "admin", 896 "wheel" 897 }; 898 899 // Reading the groups database may touch the file system. 900 ThreadRestrictions::AssertIOAllowed(); 901 902 std::set<gid_t> allowed_group_ids; 903 for (int i = 0, ie = arraysize(kAdminGroupNames); i < ie; ++i) { 904 struct group *group_record = getgrnam(kAdminGroupNames[i]); 905 if (!group_record) { 906 DPLOG(ERROR) << "Could not get the group ID of group \"" 907 << kAdminGroupNames[i] << "\"."; 908 continue; 909 } 910 911 allowed_group_ids.insert(group_record->gr_gid); 912 } 913 914 return VerifyPathControlledByUser( 915 kFileSystemRoot, path, kRootUid, allowed_group_ids); 916 } 917 #endif // defined(OS_MACOSX) && !defined(OS_IOS) 918 919 int GetMaximumPathComponentLength(const FilePath& path) { 920 ThreadRestrictions::AssertIOAllowed(); 921 return pathconf(path.value().c_str(), _PC_NAME_MAX); 922 } 923 924 #if !defined(OS_ANDROID) 925 // This is implemented in file_util_android.cc for that platform. 926 bool GetShmemTempDir(bool executable, FilePath* path) { 927 #if defined(OS_LINUX) 928 bool use_dev_shm = true; 929 if (executable) { 930 static const bool s_dev_shm_executable = DetermineDevShmExecutable(); 931 use_dev_shm = s_dev_shm_executable; 932 } 933 if (use_dev_shm) { 934 *path = FilePath("/dev/shm"); 935 return true; 936 } 937 #endif 938 return GetTempDir(path); 939 } 940 #endif // !defined(OS_ANDROID) 941 942 #if !defined(OS_MACOSX) 943 // Mac has its own implementation, this is for all other Posix systems. 944 bool CopyFile(const FilePath& from_path, const FilePath& to_path) { 945 ThreadRestrictions::AssertIOAllowed(); 946 File infile; 947 #if defined(OS_ANDROID) 948 if (from_path.IsContentUri()) { 949 infile = OpenContentUriForRead(from_path); 950 } else { 951 infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ); 952 } 953 #else 954 infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ); 955 #endif 956 if (!infile.IsValid()) 957 return false; 958 959 File outfile(to_path, File::FLAG_WRITE | File::FLAG_CREATE_ALWAYS); 960 if (!outfile.IsValid()) 961 return false; 962 963 const size_t kBufferSize = 32768; 964 std::vector<char> buffer(kBufferSize); 965 bool result = true; 966 967 while (result) { 968 ssize_t bytes_read = infile.ReadAtCurrentPos(&buffer[0], buffer.size()); 969 if (bytes_read < 0) { 970 result = false; 971 break; 972 } 973 if (bytes_read == 0) 974 break; 975 // Allow for partial writes 976 ssize_t bytes_written_per_read = 0; 977 do { 978 ssize_t bytes_written_partial = outfile.WriteAtCurrentPos( 979 &buffer[bytes_written_per_read], bytes_read - bytes_written_per_read); 980 if (bytes_written_partial < 0) { 981 result = false; 982 break; 983 } 984 bytes_written_per_read += bytes_written_partial; 985 } while (bytes_written_per_read < bytes_read); 986 } 987 988 return result; 989 } 990 #endif // !defined(OS_MACOSX) 991 992 // ----------------------------------------------------------------------------- 993 994 namespace internal { 995 996 bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { 997 ThreadRestrictions::AssertIOAllowed(); 998 // Windows compatibility: if to_path exists, from_path and to_path 999 // must be the same type, either both files, or both directories. 1000 stat_wrapper_t to_file_info; 1001 if (CallStat(to_path.value().c_str(), &to_file_info) == 0) { 1002 stat_wrapper_t from_file_info; 1003 if (CallStat(from_path.value().c_str(), &from_file_info) == 0) { 1004 if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode)) 1005 return false; 1006 } else { 1007 return false; 1008 } 1009 } 1010 1011 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) 1012 return true; 1013 1014 if (!CopyDirectory(from_path, to_path, true)) 1015 return false; 1016 1017 DeleteFile(from_path, true); 1018 return true; 1019 } 1020 1021 } // namespace internal 1022 1023 #endif // !defined(OS_NACL_NONSFI) 1024 } // namespace base 1025