OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/file_util.h" | 5 #include "base/file_util.h" |
6 | 6 |
7 #include <dirent.h> | 7 #include <dirent.h> |
8 #include <errno.h> | 8 #include <errno.h> |
9 #include <fcntl.h> | 9 #include <fcntl.h> |
10 #include <libgen.h> | 10 #include <libgen.h> |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
143 return StringPrintf(".%s.XXXXXX", base::mac::BaseBundleID()); | 143 return StringPrintf(".%s.XXXXXX", base::mac::BaseBundleID()); |
144 #endif | 144 #endif |
145 | 145 |
146 #if defined(GOOGLE_CHROME_BUILD) | 146 #if defined(GOOGLE_CHROME_BUILD) |
147 return std::string(".com.google.Chrome.XXXXXX"); | 147 return std::string(".com.google.Chrome.XXXXXX"); |
148 #else | 148 #else |
149 return std::string(".org.chromium.Chromium.XXXXXX"); | 149 return std::string(".org.chromium.Chromium.XXXXXX"); |
150 #endif | 150 #endif |
151 } | 151 } |
152 | 152 |
| 153 // Creates and opens a temporary file in |directory|, returning the |
| 154 // file descriptor. |path| is set to the temporary file path. |
| 155 // This function does NOT unlink() the file. |
| 156 int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) { |
| 157 ThreadRestrictions::AssertIOAllowed(); // For call to mkstemp(). |
| 158 *path = directory.Append(base::TempFileName()); |
| 159 const std::string& tmpdir_string = path->value(); |
| 160 // this should be OK since mkstemp just replaces characters in place |
| 161 char* buffer = const_cast<char*>(tmpdir_string.c_str()); |
| 162 |
| 163 return HANDLE_EINTR(mkstemp(buffer)); |
| 164 } |
| 165 |
| 166 #if defined(OS_LINUX) |
| 167 // Determine if /dev/shm files can be mapped and then mprotect'd PROT_EXEC. |
| 168 // This depends on the mount options used for /dev/shm, which vary among |
| 169 // different Linux distributions and possibly local configuration. It also |
| 170 // depends on details of kernel--ChromeOS uses the noexec option for /dev/shm |
| 171 // but its kernel allows mprotect with PROT_EXEC anyway. |
| 172 bool DetermineDevShmExecutable() { |
| 173 bool result = false; |
| 174 FilePath path; |
| 175 int fd = CreateAndOpenFdForTemporaryFile(FilePath("/dev/shm"), &path); |
| 176 if (fd >= 0) { |
| 177 file_util::ScopedFD shm_fd_closer(&fd); |
| 178 DeleteFile(path, false); |
| 179 long sysconf_result = sysconf(_SC_PAGESIZE); |
| 180 CHECK_GE(sysconf_result, 0); |
| 181 size_t pagesize = static_cast<size_t>(sysconf_result); |
| 182 CHECK_GE(sizeof(pagesize), sizeof(sysconf_result)); |
| 183 void *mapping = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, 0); |
| 184 if (mapping != MAP_FAILED) { |
| 185 if (mprotect(mapping, pagesize, PROT_READ | PROT_EXEC) == 0) |
| 186 result = true; |
| 187 munmap(mapping, pagesize); |
| 188 } |
| 189 } |
| 190 return result; |
| 191 } |
| 192 #endif // defined(OS_LINUX) |
| 193 |
153 } // namespace | 194 } // namespace |
154 | 195 |
155 FilePath MakeAbsoluteFilePath(const FilePath& input) { | 196 FilePath MakeAbsoluteFilePath(const FilePath& input) { |
156 ThreadRestrictions::AssertIOAllowed(); | 197 ThreadRestrictions::AssertIOAllowed(); |
157 char full_path[PATH_MAX]; | 198 char full_path[PATH_MAX]; |
158 if (realpath(input.value().c_str(), full_path) == NULL) | 199 if (realpath(input.value().c_str(), full_path) == NULL) |
159 return FilePath(); | 200 return FilePath(); |
160 return FilePath(full_path); | 201 return FilePath(full_path); |
161 } | 202 } |
162 | 203 |
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
398 // Clears the existing permission bits, and adds the new ones. | 439 // Clears the existing permission bits, and adds the new ones. |
399 mode_t updated_mode_bits = stat_buf.st_mode & ~FILE_PERMISSION_MASK; | 440 mode_t updated_mode_bits = stat_buf.st_mode & ~FILE_PERMISSION_MASK; |
400 updated_mode_bits |= mode & FILE_PERMISSION_MASK; | 441 updated_mode_bits |= mode & FILE_PERMISSION_MASK; |
401 | 442 |
402 if (HANDLE_EINTR(chmod(path.value().c_str(), updated_mode_bits)) != 0) | 443 if (HANDLE_EINTR(chmod(path.value().c_str(), updated_mode_bits)) != 0) |
403 return false; | 444 return false; |
404 | 445 |
405 return true; | 446 return true; |
406 } | 447 } |
407 | 448 |
| 449 #if !defined(OS_MACOSX) |
| 450 // This is implemented in file_util_mac.mm for Mac. |
| 451 bool GetTempDir(FilePath* path) { |
| 452 const char* tmp = getenv("TMPDIR"); |
| 453 if (tmp) { |
| 454 *path = FilePath(tmp); |
| 455 } else { |
| 456 #if defined(OS_ANDROID) |
| 457 return PathService::Get(base::DIR_CACHE, path); |
| 458 #else |
| 459 *path = FilePath("/tmp"); |
| 460 #endif |
| 461 } |
| 462 return true; |
| 463 } |
| 464 #endif // !defined(OS_MACOSX) |
| 465 |
| 466 #if !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 467 // This is implemented in file_util_mac.mm and file_util_android.cc for those |
| 468 // platforms. |
| 469 bool GetShmemTempDir(bool executable, FilePath* path) { |
| 470 #if defined(OS_LINUX) |
| 471 bool use_dev_shm = true; |
| 472 if (executable) { |
| 473 static const bool s_dev_shm_executable = DetermineDevShmExecutable(); |
| 474 use_dev_shm = s_dev_shm_executable; |
| 475 } |
| 476 if (use_dev_shm) { |
| 477 *path = FilePath("/dev/shm"); |
| 478 return true; |
| 479 } |
| 480 #endif |
| 481 return GetTempDir(path); |
| 482 } |
| 483 #endif // !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 484 |
| 485 #if !defined(OS_MACOSX) |
| 486 FilePath GetHomeDir() { |
| 487 #if defined(OS_CHROMEOS) |
| 488 if (SysInfo::IsRunningOnChromeOS()) |
| 489 return FilePath("/home/chronos/user"); |
| 490 #endif |
| 491 |
| 492 const char* home_dir = getenv("HOME"); |
| 493 if (home_dir && home_dir[0]) |
| 494 return FilePath(home_dir); |
| 495 |
| 496 #if defined(OS_ANDROID) |
| 497 DLOG(WARNING) << "OS_ANDROID: Home directory lookup not yet implemented."; |
| 498 #elif defined(USE_GLIB) && !defined(OS_CHROMEOS) |
| 499 // g_get_home_dir calls getpwent, which can fall through to LDAP calls. |
| 500 ThreadRestrictions::AssertIOAllowed(); |
| 501 |
| 502 home_dir = g_get_home_dir(); |
| 503 if (home_dir && home_dir[0]) |
| 504 return FilePath(home_dir); |
| 505 #endif |
| 506 |
| 507 FilePath rv; |
| 508 if (GetTempDir(&rv)) |
| 509 return rv; |
| 510 |
| 511 // Last resort. |
| 512 return FilePath("/tmp"); |
| 513 } |
| 514 #endif // !defined(OS_MACOSX) |
| 515 |
408 } // namespace base | 516 } // namespace base |
409 | 517 |
410 // ----------------------------------------------------------------------------- | 518 // ----------------------------------------------------------------------------- |
411 | 519 |
412 namespace file_util { | 520 namespace file_util { |
413 | 521 |
414 using base::stat_wrapper_t; | 522 using base::stat_wrapper_t; |
415 using base::CallStat; | 523 using base::CallStat; |
416 using base::CallLstat; | 524 using base::CallLstat; |
| 525 using base::CreateAndOpenFdForTemporaryFile; |
417 using base::DirectoryExists; | 526 using base::DirectoryExists; |
418 using base::FileEnumerator; | 527 using base::FileEnumerator; |
419 using base::FilePath; | 528 using base::FilePath; |
420 using base::MakeAbsoluteFilePath; | 529 using base::MakeAbsoluteFilePath; |
421 using base::RealPath; | 530 using base::RealPath; |
422 using base::VerifySpecificPathControlledByUser; | 531 using base::VerifySpecificPathControlledByUser; |
423 | 532 |
424 // Creates and opens a temporary file in |directory|, returning the | |
425 // file descriptor. |path| is set to the temporary file path. | |
426 // This function does NOT unlink() the file. | |
427 int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) { | |
428 base::ThreadRestrictions::AssertIOAllowed(); // For call to mkstemp(). | |
429 *path = directory.Append(base::TempFileName()); | |
430 const std::string& tmpdir_string = path->value(); | |
431 // this should be OK since mkstemp just replaces characters in place | |
432 char* buffer = const_cast<char*>(tmpdir_string.c_str()); | |
433 | |
434 return HANDLE_EINTR(mkstemp(buffer)); | |
435 } | |
436 | |
437 bool CreateTemporaryFile(FilePath* path) { | 533 bool CreateTemporaryFile(FilePath* path) { |
438 base::ThreadRestrictions::AssertIOAllowed(); // For call to close(). | 534 base::ThreadRestrictions::AssertIOAllowed(); // For call to close(). |
439 FilePath directory; | 535 FilePath directory; |
440 if (!GetTempDir(&directory)) | 536 if (!GetTempDir(&directory)) |
441 return false; | 537 return false; |
442 int fd = CreateAndOpenFdForTemporaryFile(directory, path); | 538 int fd = CreateAndOpenFdForTemporaryFile(directory, path); |
443 if (fd < 0) | 539 if (fd < 0) |
444 return false; | 540 return false; |
445 ignore_result(HANDLE_EINTR(close(fd))); | 541 ignore_result(HANDLE_EINTR(close(fd))); |
446 return true; | 542 return true; |
447 } | 543 } |
448 | 544 |
449 FILE* CreateAndOpenTemporaryShmemFile(FilePath* path, bool executable) { | 545 FILE* CreateAndOpenTemporaryShmemFile(FilePath* path, bool executable) { |
450 FilePath directory; | 546 FilePath directory; |
451 if (!GetShmemTempDir(&directory, executable)) | 547 if (!GetShmemTempDir(executable, &directory)) |
452 return NULL; | 548 return NULL; |
453 | 549 |
454 return CreateAndOpenTemporaryFileInDir(directory, path); | 550 return CreateAndOpenTemporaryFileInDir(directory, path); |
455 } | 551 } |
456 | 552 |
457 FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { | 553 FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { |
458 int fd = CreateAndOpenFdForTemporaryFile(dir, path); | 554 int fd = CreateAndOpenFdForTemporaryFile(dir, path); |
459 if (fd < 0) | 555 if (fd < 0) |
460 return NULL; | 556 return NULL; |
461 | 557 |
(...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
718 // directory. | 814 // directory. |
719 stat_wrapper_t file_info; | 815 stat_wrapper_t file_info; |
720 if (CallStat(real_path_result.value().c_str(), &file_info) != 0 || | 816 if (CallStat(real_path_result.value().c_str(), &file_info) != 0 || |
721 S_ISDIR(file_info.st_mode)) | 817 S_ISDIR(file_info.st_mode)) |
722 return false; | 818 return false; |
723 | 819 |
724 *normalized_path = real_path_result; | 820 *normalized_path = real_path_result; |
725 return true; | 821 return true; |
726 } | 822 } |
727 | 823 |
728 #if !defined(OS_MACOSX) | |
729 bool GetTempDir(FilePath* path) { | |
730 const char* tmp = getenv("TMPDIR"); | |
731 if (tmp) | |
732 *path = FilePath(tmp); | |
733 else | |
734 #if defined(OS_ANDROID) | |
735 return PathService::Get(base::DIR_CACHE, path); | |
736 #else | |
737 *path = FilePath("/tmp"); | |
738 #endif | |
739 return true; | |
740 } | |
741 | |
742 #if !defined(OS_ANDROID) | |
743 | |
744 #if defined(OS_LINUX) | |
745 // Determine if /dev/shm files can be mapped and then mprotect'd PROT_EXEC. | |
746 // This depends on the mount options used for /dev/shm, which vary among | |
747 // different Linux distributions and possibly local configuration. It also | |
748 // depends on details of kernel--ChromeOS uses the noexec option for /dev/shm | |
749 // but its kernel allows mprotect with PROT_EXEC anyway. | |
750 | |
751 namespace { | |
752 | |
753 bool DetermineDevShmExecutable() { | |
754 bool result = false; | |
755 FilePath path; | |
756 int fd = CreateAndOpenFdForTemporaryFile(FilePath("/dev/shm"), &path); | |
757 if (fd >= 0) { | |
758 ScopedFD shm_fd_closer(&fd); | |
759 DeleteFile(path, false); | |
760 long sysconf_result = sysconf(_SC_PAGESIZE); | |
761 CHECK_GE(sysconf_result, 0); | |
762 size_t pagesize = static_cast<size_t>(sysconf_result); | |
763 CHECK_GE(sizeof(pagesize), sizeof(sysconf_result)); | |
764 void *mapping = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, 0); | |
765 if (mapping != MAP_FAILED) { | |
766 if (mprotect(mapping, pagesize, PROT_READ | PROT_EXEC) == 0) | |
767 result = true; | |
768 munmap(mapping, pagesize); | |
769 } | |
770 } | |
771 return result; | |
772 } | |
773 | |
774 }; // namespace | |
775 #endif // defined(OS_LINUX) | |
776 | |
777 bool GetShmemTempDir(FilePath* path, bool executable) { | |
778 #if defined(OS_LINUX) | |
779 bool use_dev_shm = true; | |
780 if (executable) { | |
781 static const bool s_dev_shm_executable = DetermineDevShmExecutable(); | |
782 use_dev_shm = s_dev_shm_executable; | |
783 } | |
784 if (use_dev_shm) { | |
785 *path = FilePath("/dev/shm"); | |
786 return true; | |
787 } | |
788 #endif | |
789 return GetTempDir(path); | |
790 } | |
791 #endif // !defined(OS_ANDROID) | |
792 | |
793 FilePath GetHomeDir() { | |
794 #if defined(OS_CHROMEOS) | |
795 if (base::SysInfo::IsRunningOnChromeOS()) | |
796 return FilePath("/home/chronos/user"); | |
797 #endif | |
798 | |
799 const char* home_dir = getenv("HOME"); | |
800 if (home_dir && home_dir[0]) | |
801 return FilePath(home_dir); | |
802 | |
803 #if defined(OS_ANDROID) | |
804 DLOG(WARNING) << "OS_ANDROID: Home directory lookup not yet implemented."; | |
805 #elif defined(USE_GLIB) && !defined(OS_CHROMEOS) | |
806 // g_get_home_dir calls getpwent, which can fall through to LDAP calls. | |
807 base::ThreadRestrictions::AssertIOAllowed(); | |
808 | |
809 home_dir = g_get_home_dir(); | |
810 if (home_dir && home_dir[0]) | |
811 return FilePath(home_dir); | |
812 #endif | |
813 | |
814 FilePath rv; | |
815 if (file_util::GetTempDir(&rv)) | |
816 return rv; | |
817 | |
818 // Last resort. | |
819 return FilePath("/tmp"); | |
820 } | |
821 #endif // !defined(OS_MACOSX) | |
822 | |
823 bool VerifyPathControlledByUser(const FilePath& base, | 824 bool VerifyPathControlledByUser(const FilePath& base, |
824 const FilePath& path, | 825 const FilePath& path, |
825 uid_t owner_uid, | 826 uid_t owner_uid, |
826 const std::set<gid_t>& group_gids) { | 827 const std::set<gid_t>& group_gids) { |
827 if (base != path && !base.IsParent(path)) { | 828 if (base != path && !base.IsParent(path)) { |
828 DLOG(ERROR) << "|base| must be a subdirectory of |path|. base = \"" | 829 DLOG(ERROR) << "|base| must be a subdirectory of |path|. base = \"" |
829 << base.value() << "\", path = \"" << path.value() << "\""; | 830 << base.value() << "\", path = \"" << path.value() << "\""; |
830 return false; | 831 return false; |
831 } | 832 } |
832 | 833 |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
970 result = false; | 971 result = false; |
971 if (HANDLE_EINTR(close(outfile)) < 0) | 972 if (HANDLE_EINTR(close(outfile)) < 0) |
972 result = false; | 973 result = false; |
973 | 974 |
974 return result; | 975 return result; |
975 } | 976 } |
976 #endif // !defined(OS_MACOSX) | 977 #endif // !defined(OS_MACOSX) |
977 | 978 |
978 } // namespace internal | 979 } // namespace internal |
979 } // namespace base | 980 } // namespace base |
OLD | NEW |