[llvm] r272555 - 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.
Taewook Oh via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 13 08:54:57 PDT 2016
Author: twoh
Date: Mon Jun 13 10:54:56 2016
New Revision: 272555
URL: http://llvm.org/viewvc/llvm-project?rev=272555&view=rev
Log:
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
Corresponding clang patch: http://reviews.llvm.org/D19843
Re-commit after addressing issues with of generating too many warnings for Windows and asan test failures
Patch by Eric Niebler
Modified:
llvm/trunk/include/llvm/Support/FileSystem.h
llvm/trunk/lib/Support/Unix/Path.inc
llvm/trunk/lib/Support/Windows/Path.inc
llvm/trunk/unittests/Support/Path.cpp
Modified: llvm/trunk/include/llvm/Support/FileSystem.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/FileSystem.h?rev=272555&r1=272554&r2=272555&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/FileSystem.h (original)
+++ llvm/trunk/include/llvm/Support/FileSystem.h Mon Jun 13 10:54:56 2016
@@ -604,6 +604,12 @@ std::error_code createTemporaryFile(cons
std::error_code createUniqueDirectory(const Twine &Prefix,
SmallVectorImpl<char> &ResultPath);
+/// @brief Fetch a path to an open file, as specified by a file descriptor
+///
+/// @param FD File descriptor to a currently open file
+/// @param ResultPath The buffer into which to write the path
+std::error_code getPathFromOpenFD(int FD, SmallVectorImpl<char> &ResultPath);
+
enum OpenFlags : unsigned {
F_None = 0,
@@ -636,7 +642,8 @@ inline OpenFlags &operator|=(OpenFlags &
std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
OpenFlags Flags, unsigned Mode = 0666);
-std::error_code openFileForRead(const Twine &Name, int &ResultFD);
+std::error_code openFileForRead(const Twine &Name, int &ResultFD,
+ SmallVectorImpl<char> *RealPath = nullptr);
/// @brief Identify the type of a binary file based on how magical it is.
file_magic identify_magic(StringRef magic);
Modified: llvm/trunk/lib/Support/Unix/Path.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Unix/Path.inc?rev=272555&r1=272554&r2=272555&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Unix/Path.inc (original)
+++ llvm/trunk/lib/Support/Unix/Path.inc Mon Jun 13 10:54:56 2016
@@ -25,6 +25,9 @@
#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
@@ -47,6 +50,7 @@
#ifdef __APPLE__
#include <mach-o/dyld.h>
+#include <sys/attr.h>
#endif
// Both stdio.h and cstdio are included via different pathes and
@@ -544,13 +548,47 @@ std::error_code detail::directory_iterat
return std::error_code();
}
-std::error_code openFileForRead(const Twine &Name, int &ResultFD) {
+#if !defined(F_GETPATH)
+static bool hasProcSelfFD() {
+ // If we have a /proc filesystem mounted, we can quickly establish the
+ // real name of the file with readlink
+ static const bool Result = (::access("/proc/self/fd", R_OK) == 0);
+ return Result;
+}
+#endif
+
+std::error_code openFileForRead(const Twine &Name, int &ResultFD,
+ SmallVectorImpl<char> *RealPath) {
SmallString<128> Storage;
StringRef P = Name.toNullTerminatedStringRef(Storage);
while ((ResultFD = open(P.begin(), O_RDONLY)) < 0) {
if (errno != EINTR)
return std::error_code(errno, std::generic_category());
}
+ // Attempt to get the real name of the file, if the user asked
+ if(!RealPath)
+ return std::error_code();
+ RealPath->clear();
+#if defined(F_GETPATH)
+ // When F_GETPATH is availble, it is the quickest way to get
+ // the real path name.
+ char Buffer[MAXPATHLEN];
+ if (::fcntl(ResultFD, F_GETPATH, Buffer) != -1)
+ RealPath->append(Buffer, Buffer + strlen(Buffer));
+#else
+ char Buffer[PATH_MAX];
+ if (hasProcSelfFD()) {
+ char ProcPath[64];
+ snprintf(ProcPath, sizeof(ProcPath), "/proc/self/fd/%d", ResultFD);
+ ssize_t CharCount = ::readlink(ProcPath, Buffer, sizeof(Buffer));
+ if (CharCount > 0)
+ RealPath->append(Buffer, Buffer + CharCount);
+ } else {
+ // Use ::realpath to get the real path name
+ if (::realpath(P.begin(), Buffer) != nullptr)
+ RealPath->append(Buffer, Buffer + strlen(Buffer));
+ }
+#endif
return std::error_code();
}
@@ -584,6 +622,53 @@ std::error_code openFileForWrite(const T
return std::error_code();
}
+std::error_code getPathFromOpenFD(int FD, SmallVectorImpl<char> &ResultPath) {
+ if (FD < 0)
+ return make_error_code(errc::bad_file_descriptor);
+
+#if defined(F_GETPATH)
+ // When F_GETPATH is availble, it is the quickest way to get
+ // the path from a file descriptor.
+ ResultPath.reserve(MAXPATHLEN);
+ if (::fcntl(FD, F_GETPATH, ResultPath.begin()) == -1)
+ return std::error_code(errno, std::generic_category());
+
+ ResultPath.set_size(strlen(ResultPath.begin()));
+#else
+ // If we have a /proc filesystem mounted, we can quickly establish the
+ // real name of the file with readlink. Otherwise, we don't know how to
+ // get the filename from a file descriptor. Give up.
+ if (!fs::hasProcSelfFD())
+ return make_error_code(errc::function_not_supported);
+
+ ResultPath.reserve(PATH_MAX);
+ char ProcPath[64];
+ snprintf(ProcPath, sizeof(ProcPath), "/proc/self/fd/%d", FD);
+ ssize_t CharCount = ::readlink(ProcPath, ResultPath.begin(), ResultPath.capacity());
+ if (CharCount < 0)
+ return std::error_code(errno, std::generic_category());
+
+ // Was the filename truncated?
+ if (static_cast<size_t>(CharCount) == ResultPath.capacity()) {
+ // Use lstat to get the size of the filename
+ struct stat sb;
+ if (::lstat(ProcPath, &sb) < 0)
+ return std::error_code(errno, std::generic_category());
+
+ ResultPath.reserve(sb.st_size + 1);
+ CharCount = ::readlink(ProcPath, ResultPath.begin(), ResultPath.capacity());
+ if (CharCount < 0)
+ return std::error_code(errno, std::generic_category());
+
+ // Test for race condition: did the link size change?
+ if (CharCount > sb.st_size)
+ return std::error_code(ENAMETOOLONG, std::generic_category());
+ }
+ ResultPath.set_size(static_cast<size_t>(CharCount));
+#endif
+ return std::error_code();
+}
+
} // end namespace fs
namespace path {
Modified: llvm/trunk/lib/Support/Windows/Path.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Windows/Path.inc?rev=272555&r1=272554&r2=272555&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Windows/Path.inc (original)
+++ llvm/trunk/lib/Support/Windows/Path.inc Mon Jun 13 10:54:56 2016
@@ -707,7 +707,8 @@ std::error_code detail::directory_iterat
return std::error_code();
}
-std::error_code openFileForRead(const Twine &Name, int &ResultFD) {
+std::error_code openFileForRead(const Twine &Name, int &ResultFD,
+ SmallVectorImpl<char> *RealPath) {
SmallVector<wchar_t, 128> PathUTF16;
if (std::error_code EC = widenPath(Name, PathUTF16))
@@ -736,6 +737,22 @@ std::error_code openFileForRead(const Tw
return mapWindowsError(ERROR_INVALID_HANDLE);
}
+ // Fetch the real name of the file, if the user asked
+ if (RealPath) {
+ RealPath->clear();
+ wchar_t RealPathUTF16[MAX_PATH];
+ DWORD CountChars =
+ ::GetFinalPathNameByHandleW(H, RealPathUTF16, MAX_PATH,
+ FILE_NAME_NORMALIZED);
+ if (CountChars > 0 && CountChars < MAX_PATH) {
+ // Convert the result from UTF-16 to UTF-8.
+ SmallString<MAX_PATH> RealPathUTF8;
+ if (!UTF16ToUTF8(RealPathUTF16, CountChars, RealPathUTF8))
+ RealPath->append(RealPathUTF8.data(),
+ RealPathUTF8.data() + strlen(RealPathUTF8.data()));
+ }
+ }
+
ResultFD = FD;
return std::error_code();
}
@@ -796,6 +813,32 @@ std::error_code openFileForWrite(const T
ResultFD = FD;
return std::error_code();
}
+
+std::error_code getPathFromOpenFD(int FD, SmallVectorImpl<char> &ResultPath) {
+ HANDLE FileHandle = reinterpret_cast<HANDLE>(::_get_osfhandle(FD));
+ if (FileHandle == INVALID_HANDLE_VALUE)
+ return make_error_code(errc::bad_file_descriptor);
+
+ DWORD CharCount;
+ do {
+ CharCount = ::GetFinalPathNameByHandleA(FileHandle, ResultPath.begin(),
+ ResultPath.capacity(), FILE_NAME_NORMALIZED);
+ if (CharCount <= ResultPath.capacity())
+ break;
+ ResultPath.reserve(CharCount);
+ } while (true);
+
+ if (CharCount == 0)
+ return mapWindowsError(::GetLastError());
+
+ ResultPath.set_size(CharCount);
+
+ // On earlier Windows releases, the character count includes the terminating null.
+ if (ResultPath.back() == '\0')
+ ResultPath.pop_back();
+
+ return std::error_code();
+}
} // end namespace fs
namespace path {
@@ -930,6 +973,7 @@ std::error_code UTF16ToCurCP(const wchar
llvm::SmallVectorImpl<char> &utf8) {
return UTF16ToCodePage(CP_ACP, utf16, utf16_len, utf8);
}
+
} // end namespace windows
} // end namespace sys
} // end namespace llvm
Modified: llvm/trunk/unittests/Support/Path.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/Path.cpp?rev=272555&r1=272554&r2=272555&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/Path.cpp (original)
+++ llvm/trunk/unittests/Support/Path.cpp Mon Jun 13 10:54:56 2016
@@ -995,4 +995,61 @@ TEST(Support, ReplacePathPrefix) {
path::replace_path_prefix(Path, OldPrefix, EmptyPrefix);
EXPECT_EQ(Path, "/foo");
}
+
+TEST_F(FileSystemTest, PathFromFD) {
+ // Create a temp file.
+ int FileDescriptor;
+ SmallString<64> TempPath;
+ ASSERT_NO_ERROR(
+ fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));
+
+ // Make sure it exists.
+ ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));
+
+ // Try to get the path from the file descriptor
+ SmallString<64> ResultPath;
+ std::error_code ErrorCode =
+ fs::getPathFromOpenFD(FileDescriptor, ResultPath);
+
+ // If we succeeded, check that the paths are the same (modulo case):
+ if (!ErrorCode) {
+ // The paths returned by createTemporaryFile and getPathFromOpenFD
+ // should reference the same file on disk.
+ fs::UniqueID D1, D2;
+ ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1));
+ ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2));
+ ASSERT_EQ(D1, D2);
+ }
+
+ ::close(FileDescriptor);
+}
+
+TEST_F(FileSystemTest, OpenFileForRead) {
+ // Create a temp file.
+ int FileDescriptor;
+ SmallString<64> TempPath;
+ ASSERT_NO_ERROR(
+ fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));
+
+ // Make sure it exists.
+ ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));
+
+ // Open the file for read
+ int FileDescriptor2;
+ SmallString<64> ResultPath;
+ ASSERT_NO_ERROR(
+ fs::openFileForRead(Twine(TempPath), FileDescriptor2, &ResultPath))
+
+ // If we succeeded, check that the paths are the same (modulo case):
+ if (!ResultPath.empty()) {
+ // The paths returned by createTemporaryFile and getPathFromOpenFD
+ // should reference the same file on disk.
+ fs::UniqueID D1, D2;
+ ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1));
+ ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2));
+ ASSERT_EQ(D1, D2);
+ }
+
+ ::close(FileDescriptor);
+}
} // anonymous namespace
More information about the llvm-commits
mailing list