llvm.org GIT mirror llvm / 0331034
In openFileForRead, attempt to fetch the actual name of the file on disk -- including case -- so that clang can later warn about non-portable #include and #import directives. Differential Revision: http://reviews.llvm.org/D19842 Patch by Eric Niebler git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@271704 91177308-0d34-0410-b5e6-96231b3b80d8 Taewook Oh 4 years ago
4 changed file(s) with 196 addition(s) and 3 deletion(s). Raw diff Collapse all Expand all
603603 std::error_code createUniqueDirectory(const Twine &Prefix,
604604 SmallVectorImpl &ResultPath);
605605
606 /// @brief Fetch a path to an open file, as specified by a file descriptor
607 ///
608 /// @param FD File descriptor to a currently open file
609 /// @param ResultPath The buffer into which to write the path
610 std::error_code getPathFromOpenFD(int FD, SmallVectorImpl &ResultPath);
611
606612 enum OpenFlags : unsigned {
607613 F_None = 0,
608614
635641 std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
636642 OpenFlags Flags, unsigned Mode = 0666);
637643
638 std::error_code openFileForRead(const Twine &Name, int &ResultFD);
644 std::error_code openFileForRead(const Twine &Name, int &ResultFD,
645 SmallVectorImpl *RealPath = nullptr);
639646
640647 /// @brief Identify the type of a binary file based on how magical it is.
641648 file_magic identify_magic(StringRef magic);
2323 #endif
2424 #if HAVE_FCNTL_H
2525 #include
26 #endif
27 #ifdef HAVE_UNISTD_H
28 #include
2629 #endif
2730 #ifdef HAVE_SYS_MMAN_H
2831 #include
4649
4750 #ifdef __APPLE__
4851 #include
52 #include
4953 #endif
5054
5155 // Both stdio.h and cstdio are included via different pathes and
543547 return std::error_code();
544548 }
545549
546 std::error_code openFileForRead(const Twine &Name, int &ResultFD) {
550 #if !defined(F_GETPATH)
551 static bool hasProcSelfFD() {
552 // If we have a /proc filesystem mounted, we can quickly establish the
553 // real name of the file with readlink
554 static const bool Result = (::access("/proc/self/fd", R_OK) == 0);
555 return Result;
556 }
557 #endif
558
559 std::error_code openFileForRead(const Twine &Name, int &ResultFD,
560 SmallVectorImpl *RealPath) {
547561 SmallString<128> Storage;
548562 StringRef P = Name.toNullTerminatedStringRef(Storage);
549563 while ((ResultFD = open(P.begin(), O_RDONLY)) < 0) {
550564 if (errno != EINTR)
551565 return std::error_code(errno, std::generic_category());
552566 }
567 // Attempt to get the real name of the file, if the user asked
568 if(!RealPath)
569 return std::error_code();
570 RealPath->clear();
571 #if defined(F_GETPATH)
572 // When F_GETPATH is availble, it is the quickest way to get
573 // the real path name.
574 char Buffer[MAXPATHLEN];
575 if (::fcntl(ResultFD, F_GETPATH, Buffer) != -1)
576 RealPath->append(Buffer, Buffer + strlen(Buffer));
577 #else
578 char Buffer[PATH_MAX];
579 if (hasProcSelfFD()) {
580 char ProcPath[64];
581 snprintf(ProcPath, sizeof(ProcPath), "/proc/self/fd/%d", ResultFD);
582 ssize_t CharCount = ::readlink(ProcPath, Buffer, sizeof(Buffer));
583 if (CharCount > 0)
584 RealPath->append(Buffer, Buffer + CharCount);
585 } else {
586 // Use ::realpath to get the real path name
587 if (::realpath(P.begin(), Buffer) != nullptr)
588 RealPath->append(Buffer, Buffer + strlen(Buffer));
589 }
590 #endif
553591 return std::error_code();
554592 }
555593
580618 if (errno != EINTR)
581619 return std::error_code(errno, std::generic_category());
582620 }
621 return std::error_code();
622 }
623
624 std::error_code getPathFromOpenFD(int FD, SmallVectorImpl &ResultPath) {
625 if (FD < 0)
626 return make_error_code(errc::bad_file_descriptor);
627
628 #if defined(F_GETPATH)
629 // When F_GETPATH is availble, it is the quickest way to get
630 // the path from a file descriptor.
631 ResultPath.reserve(MAXPATHLEN);
632 if (::fcntl(FD, F_GETPATH, ResultPath.begin()) == -1)
633 return std::error_code(errno, std::generic_category());
634
635 ResultPath.set_size(strlen(ResultPath.begin()));
636 #else
637 // If we have a /proc filesystem mounted, we can quickly establish the
638 // real name of the file with readlink. Otherwise, we don't know how to
639 // get the filename from a file descriptor. Give up.
640 if (!fs::hasProcSelfFD())
641 return make_error_code(errc::function_not_supported);
642
643 ResultPath.reserve(PATH_MAX);
644 char ProcPath[64];
645 snprintf(ProcPath, sizeof(ProcPath), "/proc/self/fd/%d", FD);
646 ssize_t CharCount = ::readlink(ProcPath, ResultPath.begin(), ResultPath.capacity());
647 if (CharCount < 0)
648 return std::error_code(errno, std::generic_category());
649
650 // Was the filename truncated?
651 if (static_cast(CharCount) == ResultPath.capacity()) {
652 // Use lstat to get the size of the filename
653 struct stat sb;
654 if (::lstat(ProcPath, &sb) < 0)
655 return std::error_code(errno, std::generic_category());
656
657 ResultPath.reserve(sb.st_size + 1);
658 CharCount = ::readlink(ProcPath, ResultPath.begin(), ResultPath.capacity());
659 if (CharCount < 0)
660 return std::error_code(errno, std::generic_category());
661
662 // Test for race condition: did the link size change?
663 if (CharCount > sb.st_size)
664 return std::error_code(ENAMETOOLONG, std::generic_category());
665 }
666 ResultPath.set_size(static_cast(CharCount));
667 #endif
583668 return std::error_code();
584669 }
585670
706706 return std::error_code();
707707 }
708708
709 std::error_code openFileForRead(const Twine &Name, int &ResultFD) {
709 std::error_code openFileForRead(const Twine &Name, int &ResultFD,
710 SmallVectorImpl *RealPath) {
710711 SmallVector PathUTF16;
711712
712713 if (std::error_code EC = widenPath(Name, PathUTF16))
735736 return mapWindowsError(ERROR_INVALID_HANDLE);
736737 }
737738
739 // Fetch the real name of the file, if the user asked
740 if (RealPath) {
741 RealPath->clear();
742 wchar_t RealPathUTF16[MAX_PATH];
743 DWORD CountChars =
744 ::GetFinalPathNameByHandleW(H, RealPathUTF16, MAX_PATH,
745 FILE_NAME_NORMALIZED);
746 if (CountChars > 0 && CountChars < MAX_PATH) {
747 // Convert the result from UTF-16 to UTF-8.
748 SmallString RealPathUTF8;
749 if (!UTF16ToUTF8(RealPathUTF16, CountChars, RealPathUTF8))
750 RealPath->append(RealPathUTF8.data(),
751 RealPathUTF8.data() + strlen(RealPathUTF8.data()));
752 }
753 }
754
738755 ResultFD = FD;
739756 return std::error_code();
740757 }
795812 ResultFD = FD;
796813 return std::error_code();
797814 }
815
816 std::error_code getPathFromOpenFD(int FD, SmallVectorImpl &ResultPath) {
817 HANDLE FileHandle = reinterpret_cast(::_get_osfhandle(FD));
818 if (FileHandle == INVALID_HANDLE_VALUE)
819 return make_error_code(errc::bad_file_descriptor);
820
821 DWORD CharCount;
822 do {
823 CharCount = ::GetFinalPathNameByHandleA(FileHandle, ResultPath.begin(),
824 ResultPath.capacity(), FILE_NAME_NORMALIZED);
825 if (CharCount <= ResultPath.capacity())
826 break;
827 ResultPath.reserve(CharCount);
828 } while (true);
829
830 if (CharCount == 0)
831 return mapWindowsError(::GetLastError());
832
833 ResultPath.set_size(CharCount);
834
835 // On earlier Windows releases, the character count includes the terminating null.
836 if (ResultPath.back() == '\0')
837 ResultPath.pop_back();
838
839 return std::error_code();
840 }
798841 } // end namespace fs
799842
800843 namespace path {
929972 llvm::SmallVectorImpl &utf8) {
930973 return UTF16ToCodePage(CP_ACP, utf16, utf16_len, utf8);
931974 }
975
932976 } // end namespace windows
933977 } // end namespace sys
934978 } // end namespace llvm
994994 path::replace_path_prefix(Path, OldPrefix, EmptyPrefix);
995995 EXPECT_EQ(Path, "/foo");
996996 }
997
998 TEST_F(FileSystemTest, PathFromFD) {
999 // Create a temp file.
1000 int FileDescriptor;
1001 SmallString<64> TempPath;
1002 ASSERT_NO_ERROR(
1003 fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));
1004
1005 // Make sure it exists.
1006 ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));
1007
1008 // Try to get the path from the file descriptor
1009 SmallString<64> ResultPath;
1010 std::error_code ErrorCode =
1011 fs::getPathFromOpenFD(FileDescriptor, ResultPath);
1012
1013 // If we succeeded, check that the paths are the same (modulo case):
1014 if (!ErrorCode) {
1015 // The paths returned by createTemporaryFile and getPathFromOpenFD
1016 // should reference the same file on disk.
1017 fs::UniqueID D1, D2;
1018 ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1));
1019 ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2));
1020 ASSERT_EQ(D1, D2);
1021 }
1022
1023 ::close(FileDescriptor);
1024 }
1025
1026 TEST_F(FileSystemTest, OpenFileForRead) {
1027 // Create a temp file.
1028 int FileDescriptor;
1029 SmallString<64> TempPath;
1030 ASSERT_NO_ERROR(
1031 fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));
1032
1033 // Make sure it exists.
1034 ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));
1035
1036 // Open the file for read
1037 int FileDescriptor2;
1038 SmallString<64> ResultPath;
1039 ASSERT_NO_ERROR(
1040 fs::openFileForRead(Twine(TempPath), FileDescriptor2, &ResultPath))
1041
1042 // If we succeeded, check that the paths are the same (modulo case):
1043 if (!ResultPath.empty()) {
1044 // The paths returned by createTemporaryFile and getPathFromOpenFD
1045 // should reference the same file on disk.
1046 fs::UniqueID D1, D2;
1047 ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1));
1048 ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2));
1049 ASSERT_EQ(D1, D2);
1050 }
1051
1052 ::close(FileDescriptor);
1053 }
9971054 } // anonymous namespace