Skip to content

Commit dfec58e

Browse files
committedJun 3, 2016
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 llvm-svn: 271704
1 parent 5859a9e commit dfec58e

File tree

4 files changed

+196
-3
lines changed

4 files changed

+196
-3
lines changed
 

‎llvm/include/llvm/Support/FileSystem.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,12 @@ std::error_code createTemporaryFile(const Twine &Prefix, StringRef Suffix,
604604
std::error_code createUniqueDirectory(const Twine &Prefix,
605605
SmallVectorImpl<char> &ResultPath);
606606

607+
/// @brief Fetch a path to an open file, as specified by a file descriptor
608+
///
609+
/// @param FD File descriptor to a currently open file
610+
/// @param ResultPath The buffer into which to write the path
611+
std::error_code getPathFromOpenFD(int FD, SmallVectorImpl<char> &ResultPath);
612+
607613
enum OpenFlags : unsigned {
608614
F_None = 0,
609615

@@ -636,7 +642,8 @@ inline OpenFlags &operator|=(OpenFlags &A, OpenFlags B) {
636642
std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
637643
OpenFlags Flags, unsigned Mode = 0666);
638644

639-
std::error_code openFileForRead(const Twine &Name, int &ResultFD);
645+
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
646+
SmallVectorImpl<char> *RealPath = nullptr);
640647

641648
/// @brief Identify the type of a binary file based on how magical it is.
642649
file_magic identify_magic(StringRef magic);

‎llvm/lib/Support/Unix/Path.inc

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
#if HAVE_FCNTL_H
2626
#include <fcntl.h>
2727
#endif
28+
#ifdef HAVE_UNISTD_H
29+
#include <unistd.h>
30+
#endif
2831
#ifdef HAVE_SYS_MMAN_H
2932
#include <sys/mman.h>
3033
#endif
@@ -47,6 +50,7 @@
4750

4851
#ifdef __APPLE__
4952
#include <mach-o/dyld.h>
53+
#include <sys/attr.h>
5054
#endif
5155

5256
// Both stdio.h and cstdio are included via different pathes and
@@ -544,13 +548,47 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &it) {
544548
return std::error_code();
545549
}
546550

547-
std::error_code openFileForRead(const Twine &Name, int &ResultFD) {
551+
#if !defined(F_GETPATH)
552+
static bool hasProcSelfFD() {
553+
// If we have a /proc filesystem mounted, we can quickly establish the
554+
// real name of the file with readlink
555+
static const bool Result = (::access("/proc/self/fd", R_OK) == 0);
556+
return Result;
557+
}
558+
#endif
559+
560+
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
561+
SmallVectorImpl<char> *RealPath) {
548562
SmallString<128> Storage;
549563
StringRef P = Name.toNullTerminatedStringRef(Storage);
550564
while ((ResultFD = open(P.begin(), O_RDONLY)) < 0) {
551565
if (errno != EINTR)
552566
return std::error_code(errno, std::generic_category());
553567
}
568+
// Attempt to get the real name of the file, if the user asked
569+
if(!RealPath)
570+
return std::error_code();
571+
RealPath->clear();
572+
#if defined(F_GETPATH)
573+
// When F_GETPATH is availble, it is the quickest way to get
574+
// the real path name.
575+
char Buffer[MAXPATHLEN];
576+
if (::fcntl(ResultFD, F_GETPATH, Buffer) != -1)
577+
RealPath->append(Buffer, Buffer + strlen(Buffer));
578+
#else
579+
char Buffer[PATH_MAX];
580+
if (hasProcSelfFD()) {
581+
char ProcPath[64];
582+
snprintf(ProcPath, sizeof(ProcPath), "/proc/self/fd/%d", ResultFD);
583+
ssize_t CharCount = ::readlink(ProcPath, Buffer, sizeof(Buffer));
584+
if (CharCount > 0)
585+
RealPath->append(Buffer, Buffer + CharCount);
586+
} else {
587+
// Use ::realpath to get the real path name
588+
if (::realpath(P.begin(), Buffer) != nullptr)
589+
RealPath->append(Buffer, Buffer + strlen(Buffer));
590+
}
591+
#endif
554592
return std::error_code();
555593
}
556594

@@ -584,6 +622,53 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
584622
return std::error_code();
585623
}
586624

625+
std::error_code getPathFromOpenFD(int FD, SmallVectorImpl<char> &ResultPath) {
626+
if (FD < 0)
627+
return make_error_code(errc::bad_file_descriptor);
628+
629+
#if defined(F_GETPATH)
630+
// When F_GETPATH is availble, it is the quickest way to get
631+
// the path from a file descriptor.
632+
ResultPath.reserve(MAXPATHLEN);
633+
if (::fcntl(FD, F_GETPATH, ResultPath.begin()) == -1)
634+
return std::error_code(errno, std::generic_category());
635+
636+
ResultPath.set_size(strlen(ResultPath.begin()));
637+
#else
638+
// If we have a /proc filesystem mounted, we can quickly establish the
639+
// real name of the file with readlink. Otherwise, we don't know how to
640+
// get the filename from a file descriptor. Give up.
641+
if (!fs::hasProcSelfFD())
642+
return make_error_code(errc::function_not_supported);
643+
644+
ResultPath.reserve(PATH_MAX);
645+
char ProcPath[64];
646+
snprintf(ProcPath, sizeof(ProcPath), "/proc/self/fd/%d", FD);
647+
ssize_t CharCount = ::readlink(ProcPath, ResultPath.begin(), ResultPath.capacity());
648+
if (CharCount < 0)
649+
return std::error_code(errno, std::generic_category());
650+
651+
// Was the filename truncated?
652+
if (static_cast<size_t>(CharCount) == ResultPath.capacity()) {
653+
// Use lstat to get the size of the filename
654+
struct stat sb;
655+
if (::lstat(ProcPath, &sb) < 0)
656+
return std::error_code(errno, std::generic_category());
657+
658+
ResultPath.reserve(sb.st_size + 1);
659+
CharCount = ::readlink(ProcPath, ResultPath.begin(), ResultPath.capacity());
660+
if (CharCount < 0)
661+
return std::error_code(errno, std::generic_category());
662+
663+
// Test for race condition: did the link size change?
664+
if (CharCount > sb.st_size)
665+
return std::error_code(ENAMETOOLONG, std::generic_category());
666+
}
667+
ResultPath.set_size(static_cast<size_t>(CharCount));
668+
#endif
669+
return std::error_code();
670+
}
671+
587672
} // end namespace fs
588673

589674
namespace path {

‎llvm/lib/Support/Windows/Path.inc

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,8 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &it) {
707707
return std::error_code();
708708
}
709709

710-
std::error_code openFileForRead(const Twine &Name, int &ResultFD) {
710+
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
711+
SmallVectorImpl<char> *RealPath) {
711712
SmallVector<wchar_t, 128> PathUTF16;
712713

713714
if (std::error_code EC = widenPath(Name, PathUTF16))
@@ -736,6 +737,22 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD) {
736737
return mapWindowsError(ERROR_INVALID_HANDLE);
737738
}
738739

740+
// Fetch the real name of the file, if the user asked
741+
if (RealPath) {
742+
RealPath->clear();
743+
wchar_t RealPathUTF16[MAX_PATH];
744+
DWORD CountChars =
745+
::GetFinalPathNameByHandleW(H, RealPathUTF16, MAX_PATH,
746+
FILE_NAME_NORMALIZED);
747+
if (CountChars > 0 && CountChars < MAX_PATH) {
748+
// Convert the result from UTF-16 to UTF-8.
749+
SmallString<MAX_PATH> RealPathUTF8;
750+
if (!UTF16ToUTF8(RealPathUTF16, CountChars, RealPathUTF8))
751+
RealPath->append(RealPathUTF8.data(),
752+
RealPathUTF8.data() + strlen(RealPathUTF8.data()));
753+
}
754+
}
755+
739756
ResultFD = FD;
740757
return std::error_code();
741758
}
@@ -796,6 +813,32 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
796813
ResultFD = FD;
797814
return std::error_code();
798815
}
816+
817+
std::error_code getPathFromOpenFD(int FD, SmallVectorImpl<char> &ResultPath) {
818+
HANDLE FileHandle = reinterpret_cast<HANDLE>(::_get_osfhandle(FD));
819+
if (FileHandle == INVALID_HANDLE_VALUE)
820+
return make_error_code(errc::bad_file_descriptor);
821+
822+
DWORD CharCount;
823+
do {
824+
CharCount = ::GetFinalPathNameByHandleA(FileHandle, ResultPath.begin(),
825+
ResultPath.capacity(), FILE_NAME_NORMALIZED);
826+
if (CharCount <= ResultPath.capacity())
827+
break;
828+
ResultPath.reserve(CharCount);
829+
} while (true);
830+
831+
if (CharCount == 0)
832+
return mapWindowsError(::GetLastError());
833+
834+
ResultPath.set_size(CharCount);
835+
836+
// On earlier Windows releases, the character count includes the terminating null.
837+
if (ResultPath.back() == '\0')
838+
ResultPath.pop_back();
839+
840+
return std::error_code();
841+
}
799842
} // end namespace fs
800843

801844
namespace path {
@@ -930,6 +973,7 @@ std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
930973
llvm::SmallVectorImpl<char> &utf8) {
931974
return UTF16ToCodePage(CP_ACP, utf16, utf16_len, utf8);
932975
}
976+
933977
} // end namespace windows
934978
} // end namespace sys
935979
} // end namespace llvm

‎llvm/unittests/Support/Path.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -995,4 +995,61 @@ TEST(Support, ReplacePathPrefix) {
995995
path::replace_path_prefix(Path, OldPrefix, EmptyPrefix);
996996
EXPECT_EQ(Path, "/foo");
997997
}
998+
999+
TEST_F(FileSystemTest, PathFromFD) {
1000+
// Create a temp file.
1001+
int FileDescriptor;
1002+
SmallString<64> TempPath;
1003+
ASSERT_NO_ERROR(
1004+
fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));
1005+
1006+
// Make sure it exists.
1007+
ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));
1008+
1009+
// Try to get the path from the file descriptor
1010+
SmallString<64> ResultPath;
1011+
std::error_code ErrorCode =
1012+
fs::getPathFromOpenFD(FileDescriptor, ResultPath);
1013+
1014+
// If we succeeded, check that the paths are the same (modulo case):
1015+
if (!ErrorCode) {
1016+
// The paths returned by createTemporaryFile and getPathFromOpenFD
1017+
// should reference the same file on disk.
1018+
fs::UniqueID D1, D2;
1019+
ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1));
1020+
ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2));
1021+
ASSERT_EQ(D1, D2);
1022+
}
1023+
1024+
::close(FileDescriptor);
1025+
}
1026+
1027+
TEST_F(FileSystemTest, OpenFileForRead) {
1028+
// Create a temp file.
1029+
int FileDescriptor;
1030+
SmallString<64> TempPath;
1031+
ASSERT_NO_ERROR(
1032+
fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));
1033+
1034+
// Make sure it exists.
1035+
ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));
1036+
1037+
// Open the file for read
1038+
int FileDescriptor2;
1039+
SmallString<64> ResultPath;
1040+
ASSERT_NO_ERROR(
1041+
fs::openFileForRead(Twine(TempPath), FileDescriptor2, &ResultPath))
1042+
1043+
// If we succeeded, check that the paths are the same (modulo case):
1044+
if (!ResultPath.empty()) {
1045+
// The paths returned by createTemporaryFile and getPathFromOpenFD
1046+
// should reference the same file on disk.
1047+
fs::UniqueID D1, D2;
1048+
ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1));
1049+
ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2));
1050+
ASSERT_EQ(D1, D2);
1051+
}
1052+
1053+
::close(FileDescriptor);
1054+
}
9981055
} // anonymous namespace

0 commit comments

Comments
 (0)