[llvm] r295768 - [Support] Add a function to check if a file resides locally.
Zachary Turner via llvm-commits
llvm-commits at lists.llvm.org
Tue Feb 21 12:55:47 PST 2017
Author: zturner
Date: Tue Feb 21 14:55:47 2017
New Revision: 295768
URL: http://llvm.org/viewvc/llvm-project?rev=295768&view=rev
Log:
[Support] Add a function to check if a file resides locally.
Differential Revision: https://reviews.llvm.org/D30010
Modified:
llvm/trunk/include/llvm/Support/FileSystem.h
llvm/trunk/include/llvm/Support/MemoryBuffer.h
llvm/trunk/lib/Support/MemoryBuffer.cpp
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=295768&r1=295767&r2=295768&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/FileSystem.h (original)
+++ llvm/trunk/include/llvm/Support/FileSystem.h Tue Feb 21 14:55:47 2017
@@ -464,6 +464,32 @@ inline bool equivalent(const Twine &A, c
return !equivalent(A, B, result) && result;
}
+/// @brief Is the file mounted on a local filesystem?
+///
+/// @param path Input path.
+/// @param result Set to true if \a path is on fixed media such as a hard disk,
+/// false if it is not.
+/// @returns errc::success if result has been successfully set, otherwise a
+/// platform specific error_code.
+std::error_code is_local(const Twine &path, bool &result);
+
+/// @brief Version of is_local accepting an open file descriptor.
+std::error_code is_local(int FD, bool &result);
+
+/// @brief Simpler version of is_local for clients that don't need to
+/// differentiate between an error and false.
+inline bool is_local(const Twine &Path) {
+ bool Result;
+ return !is_local(Path, Result) && Result;
+}
+
+/// @brief Simpler version of is_local accepting an open file descriptor for
+/// clients that don't need to differentiate between an error and false.
+inline bool is_local(int FD) {
+ bool Result;
+ return !is_local(FD, Result) && Result;
+}
+
/// @brief Does status represent a directory?
///
/// @param status A file_status previously returned from status.
Modified: llvm/trunk/include/llvm/Support/MemoryBuffer.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/MemoryBuffer.h?rev=295768&r1=295767&r2=295768&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/MemoryBuffer.h (original)
+++ llvm/trunk/include/llvm/Support/MemoryBuffer.h Tue Feb 21 14:55:47 2017
@@ -69,12 +69,12 @@ public:
/// means that the client knows that the file exists and that it has the
/// specified size.
///
- /// \param IsVolatileSize Set to true to indicate that the file size may be
- /// changing, e.g. when libclang tries to parse while the user is
- /// editing/updating the file.
+ /// \param IsVolatile Set to true to indicate that the contents of the file
+ /// can change outside the user's control, e.g. when libclang tries to parse
+ /// while the user is editing/updating the file or if the file is on an NFS.
static ErrorOr<std::unique_ptr<MemoryBuffer>>
getFile(const Twine &Filename, int64_t FileSize = -1,
- bool RequiresNullTerminator = true, bool IsVolatileSize = false);
+ bool RequiresNullTerminator = true, bool IsVolatile = false);
/// Read all of the specified file into a MemoryBuffer as a stream
/// (i.e. until EOF reached). This is useful for special files that
@@ -87,17 +87,17 @@ public:
/// Since this is in the middle of a file, the buffer is not null terminated.
static ErrorOr<std::unique_ptr<MemoryBuffer>>
getOpenFileSlice(int FD, const Twine &Filename, uint64_t MapSize,
- int64_t Offset);
+ int64_t Offset, bool IsVolatile = false);
/// Given an already-open file descriptor, read the file and return a
/// MemoryBuffer.
///
- /// \param IsVolatileSize Set to true to indicate that the file size may be
- /// changing, e.g. when libclang tries to parse while the user is
- /// editing/updating the file.
+ /// \param IsVolatile Set to true to indicate that the contents of the file
+ /// can change outside the user's control, e.g. when libclang tries to parse
+ /// while the user is editing/updating the file or if the file is on an NFS.
static ErrorOr<std::unique_ptr<MemoryBuffer>>
getOpenFile(int FD, const Twine &Filename, uint64_t FileSize,
- bool RequiresNullTerminator = true, bool IsVolatileSize = false);
+ bool RequiresNullTerminator = true, bool IsVolatile = false);
/// Open the specified memory range as a MemoryBuffer. Note that InputData
/// must be null terminated if RequiresNullTerminator is true.
@@ -136,7 +136,7 @@ public:
/// Map a subrange of the specified file as a MemoryBuffer.
static ErrorOr<std::unique_ptr<MemoryBuffer>>
- getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset);
+ getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset, bool IsVolatile = false);
//===--------------------------------------------------------------------===//
// Provided for performance analysis.
Modified: llvm/trunk/lib/Support/MemoryBuffer.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/MemoryBuffer.cpp?rev=295768&r1=295767&r2=295768&view=diff
==============================================================================
--- llvm/trunk/lib/Support/MemoryBuffer.cpp (original)
+++ llvm/trunk/lib/Support/MemoryBuffer.cpp Tue Feb 21 14:55:47 2017
@@ -103,7 +103,7 @@ public:
static ErrorOr<std::unique_ptr<MemoryBuffer>>
getFileAux(const Twine &Filename, int64_t FileSize, uint64_t MapSize,
- uint64_t Offset, bool RequiresNullTerminator, bool IsVolatileSize);
+ uint64_t Offset, bool RequiresNullTerminator, bool IsVolatile);
std::unique_ptr<MemoryBuffer>
MemoryBuffer::getMemBuffer(StringRef InputData, StringRef BufferName,
@@ -178,8 +178,8 @@ MemoryBuffer::getFileOrSTDIN(const Twine
ErrorOr<std::unique_ptr<MemoryBuffer>>
MemoryBuffer::getFileSlice(const Twine &FilePath, uint64_t MapSize,
- uint64_t Offset) {
- return getFileAux(FilePath, -1, MapSize, Offset, false, false);
+ uint64_t Offset, bool IsVolatile) {
+ return getFileAux(FilePath, -1, MapSize, Offset, false, IsVolatile);
}
@@ -254,19 +254,19 @@ getMemoryBufferForStream(int FD, const T
ErrorOr<std::unique_ptr<MemoryBuffer>>
MemoryBuffer::getFile(const Twine &Filename, int64_t FileSize,
- bool RequiresNullTerminator, bool IsVolatileSize) {
+ bool RequiresNullTerminator, bool IsVolatile) {
return getFileAux(Filename, FileSize, FileSize, 0,
- RequiresNullTerminator, IsVolatileSize);
+ RequiresNullTerminator, IsVolatile);
}
static ErrorOr<std::unique_ptr<MemoryBuffer>>
getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize,
uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator,
- bool IsVolatileSize);
+ bool IsVolatile);
static ErrorOr<std::unique_ptr<MemoryBuffer>>
getFileAux(const Twine &Filename, int64_t FileSize, uint64_t MapSize,
- uint64_t Offset, bool RequiresNullTerminator, bool IsVolatileSize) {
+ uint64_t Offset, bool RequiresNullTerminator, bool IsVolatile) {
int FD;
std::error_code EC = sys::fs::openFileForRead(Filename, FD);
if (EC)
@@ -274,7 +274,7 @@ getFileAux(const Twine &Filename, int64_
ErrorOr<std::unique_ptr<MemoryBuffer>> Ret =
getOpenFileImpl(FD, Filename, FileSize, MapSize, Offset,
- RequiresNullTerminator, IsVolatileSize);
+ RequiresNullTerminator, IsVolatile);
close(FD);
return Ret;
}
@@ -285,11 +285,11 @@ static bool shouldUseMmap(int FD,
off_t Offset,
bool RequiresNullTerminator,
int PageSize,
- bool IsVolatileSize) {
+ bool IsVolatile) {
// mmap may leave the buffer without null terminator if the file size changed
// by the time the last page is mapped in, so avoid it if the file size is
// likely to change.
- if (IsVolatileSize)
+ if (IsVolatile)
return false;
// We don't use mmap for small files because this can severely fragment our
@@ -300,7 +300,6 @@ static bool shouldUseMmap(int FD,
if (!RequiresNullTerminator)
return true;
-
// If we don't know the file size, use fstat to find out. fstat on an open
// file descriptor is cheaper than stat on a random path.
// FIXME: this chunk of code is duplicated, but it avoids a fstat when
@@ -338,7 +337,7 @@ static bool shouldUseMmap(int FD,
static ErrorOr<std::unique_ptr<MemoryBuffer>>
getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize,
uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator,
- bool IsVolatileSize) {
+ bool IsVolatile) {
static int PageSize = sys::Process::getPageSize();
// Default is to map the full file.
@@ -365,7 +364,7 @@ getOpenFileImpl(int FD, const Twine &Fil
}
if (shouldUseMmap(FD, FileSize, MapSize, Offset, RequiresNullTerminator,
- PageSize, IsVolatileSize)) {
+ PageSize, IsVolatile)) {
std::error_code EC;
std::unique_ptr<MemoryBuffer> Result(
new (NamedBufferAlloc(Filename))
@@ -415,17 +414,16 @@ getOpenFileImpl(int FD, const Twine &Fil
ErrorOr<std::unique_ptr<MemoryBuffer>>
MemoryBuffer::getOpenFile(int FD, const Twine &Filename, uint64_t FileSize,
- bool RequiresNullTerminator, bool IsVolatileSize) {
+ bool RequiresNullTerminator, bool IsVolatile) {
return getOpenFileImpl(FD, Filename, FileSize, FileSize, 0,
- RequiresNullTerminator, IsVolatileSize);
+ RequiresNullTerminator, IsVolatile);
}
ErrorOr<std::unique_ptr<MemoryBuffer>>
MemoryBuffer::getOpenFileSlice(int FD, const Twine &Filename, uint64_t MapSize,
- int64_t Offset) {
+ int64_t Offset, bool IsVolatile) {
assert(MapSize != uint64_t(-1));
- return getOpenFileImpl(FD, Filename, -1, MapSize, Offset, false,
- /*IsVolatileSize*/ false);
+ return getOpenFileImpl(FD, Filename, -1, MapSize, Offset, false, IsVolatile);
}
ErrorOr<std::unique_ptr<MemoryBuffer>> MemoryBuffer::getSTDIN() {
Modified: llvm/trunk/lib/Support/Unix/Path.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Unix/Path.inc?rev=295768&r1=295767&r2=295768&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Unix/Path.inc (original)
+++ llvm/trunk/lib/Support/Unix/Path.inc Tue Feb 21 14:55:47 2017
@@ -65,23 +65,32 @@
#endif
#include <sys/types.h>
-#if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__ANDROID__)
+#if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && \
+ !defined(__linux__)
#include <sys/statvfs.h>
#define STATVFS statvfs
+#define FSTATVFS fstatvfs
#define STATVFS_F_FRSIZE(vfs) vfs.f_frsize
#else
-#ifdef __OpenBSD__
+#if defined(__OpenBSD__) || defined(__FreeBSD__)
#include <sys/param.h>
#include <sys/mount.h>
-#elif defined(__ANDROID__)
+#elif defined(__linux__)
+#include <linux/magic.h>
#include <sys/vfs.h>
#else
#include <sys/mount.h>
#endif
#define STATVFS statfs
+#define FSTATVFS fstatfs
#define STATVFS_F_FRSIZE(vfs) static_cast<uint64_t>(vfs.f_bsize)
#endif
+#if defined(__NetBSD__)
+#define STATVFS_F_FLAG(vfs) (vfs).f_flag
+#else
+#define STATVFS_F_FLAG(vfs) (vfs).f_flags
+#endif
using namespace llvm;
@@ -335,6 +344,40 @@ std::error_code remove(const Twine &path
return std::error_code();
}
+static bool is_local_impl(struct STATVFS &Vfs) {
+#if defined(__linux__)
+ constexpr uint32_t CIFS_MAGIC_NUMBER = 0xFF534D42;
+ switch ((uint32_t)Vfs.f_type) {
+ case NFS_SUPER_MAGIC:
+ case SMB_SUPER_MAGIC:
+ case CIFS_MAGIC_NUMBER:
+ return false;
+ default:
+ return true;
+ }
+#else
+ return !!(STATVFS_F_FLAG(Vfs) & MNT_LOCAL);
+#endif
+}
+
+std::error_code is_local(const Twine &Path, bool &Result) {
+ struct STATVFS Vfs;
+ if (::STATVFS(Path.str().c_str(), &Vfs))
+ return std::error_code(errno, std::generic_category());
+
+ Result = is_local_impl(Vfs);
+ return std::error_code();
+}
+
+std::error_code is_local(int FD, bool &Result) {
+ struct STATVFS Vfs;
+ if (::FSTATVFS(FD, &Vfs))
+ return std::error_code(errno, std::generic_category());
+
+ Result = is_local_impl(Vfs);
+ return std::error_code();
+}
+
std::error_code rename(const Twine &from, const Twine &to) {
// Get arguments.
SmallString<128> from_storage;
@@ -491,6 +534,23 @@ std::error_code mapped_file_region::init
int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE;
int prot = (Mode == readonly) ? PROT_READ : (PROT_READ | PROT_WRITE);
+#if defined(__APPLE__)
+//----------------------------------------------------------------------
+// Newer versions of MacOSX have a flag that will allow us to read from
+// binaries whose code signature is invalid without crashing by using
+// the MAP_RESILIENT_CODESIGN flag. Also if a file from removable media
+// is mapped we can avoid crashing and return zeroes to any pages we try
+// to read if the media becomes unavailable by using the
+// MAP_RESILIENT_MEDIA flag.
+//----------------------------------------------------------------------
+#if defined(MAP_RESILIENT_CODESIGN)
+ flags |= MAP_RESILIENT_CODESIGN;
+#endif
+#if defined(MAP_RESILIENT_MEDIA)
+ flags |= MAP_RESILIENT_MEDIA;
+#endif
+#endif // #if defined (__APPLE__)
+
Mapping = ::mmap(nullptr, Size, prot, flags, FD, Offset);
if (Mapping == MAP_FAILED)
return std::error_code(errno, std::generic_category());
Modified: llvm/trunk/lib/Support/Windows/Path.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Windows/Path.inc?rev=295768&r1=295767&r2=295768&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Windows/Path.inc (original)
+++ llvm/trunk/lib/Support/Windows/Path.inc Tue Feb 21 14:55:47 2017
@@ -277,6 +277,80 @@ std::error_code remove(const Twine &path
return std::error_code();
}
+static std::error_code is_local_internal(SmallVectorImpl<wchar_t> &Path,
+ bool &Result) {
+ SmallVector<wchar_t, 128> VolumePath;
+ size_t Len = 128;
+ while (true) {
+ VolumePath.resize(Len);
+ BOOL Success =
+ ::GetVolumePathNameW(Path.data(), VolumePath.data(), VolumePath.size());
+
+ if (Success)
+ break;
+
+ DWORD Err = ::GetLastError();
+ if (Err != ERROR_INSUFFICIENT_BUFFER)
+ return mapWindowsError(Err);
+
+ Len *= 2;
+ }
+ // If the output buffer has exactly enough space for the path name, but not
+ // the null terminator, it will leave the output unterminated. Push a null
+ // terminator onto the end to ensure that this never happens.
+ VolumePath.push_back(L'\0');
+ VolumePath.set_size(wcslen(VolumePath.data()));
+ const wchar_t *P = VolumePath.data();
+
+ UINT Type = ::GetDriveTypeW(P);
+ switch (Type) {
+ case DRIVE_FIXED:
+ Result = true;
+ return std::error_code();
+ case DRIVE_REMOTE:
+ case DRIVE_CDROM:
+ case DRIVE_RAMDISK:
+ case DRIVE_REMOVABLE:
+ Result = false;
+ return std::error_code();
+ default:
+ return make_error_code(errc::no_such_file_or_directory);
+ }
+ llvm_unreachable("Unreachable!");
+}
+
+std::error_code is_local(const Twine &path, bool &result) {
+ if (!llvm::sys::fs::exists(path) || !llvm::sys::path::has_root_path(path))
+ return make_error_code(errc::no_such_file_or_directory);
+
+ SmallString<128> Storage;
+ StringRef P = path.toStringRef(Storage);
+
+ // Convert to utf-16.
+ SmallVector<wchar_t, 128> WidePath;
+ if (std::error_code ec = widenPath(P, WidePath))
+ return ec;
+ return is_local_internal(WidePath, result);
+}
+
+std::error_code is_local(int FD, bool &Result) {
+ SmallVector<wchar_t, 128> FinalPath;
+ HANDLE Handle = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
+
+ size_t Len = 128;
+ do {
+ FinalPath.reserve(Len);
+ Len = ::GetFinalPathNameByHandleW(Handle, FinalPath.data(),
+ FinalPath.capacity() - 1, VOLUME_NAME_NT);
+ if (Len == 0)
+ return mapWindowsError(::GetLastError());
+ } while (Len > FinalPath.capacity());
+
+ FinalPath.set_size(Len);
+
+ return is_local_internal(FinalPath, Result);
+}
+
std::error_code rename(const Twine &from, const Twine &to) {
// Convert to utf-16.
SmallVector<wchar_t, 128> wide_from;
Modified: llvm/trunk/unittests/Support/Path.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/Path.cpp?rev=295768&r1=295767&r2=295768&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/Path.cpp (original)
+++ llvm/trunk/unittests/Support/Path.cpp Tue Feb 21 14:55:47 2017
@@ -1136,6 +1136,28 @@ TEST_F(FileSystemTest, OpenFileForRead)
::close(FileDescriptor);
}
+TEST_F(FileSystemTest, is_local) {
+ SmallString<128> CurrentPath;
+ ASSERT_NO_ERROR(fs::current_path(CurrentPath));
+
+ bool Result;
+ ASSERT_NO_ERROR(fs::is_local(CurrentPath, Result));
+ EXPECT_TRUE(Result);
+ EXPECT_TRUE(fs::is_local(CurrentPath));
+
+ int FD;
+ SmallString<64> TempPath;
+ ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));
+ FileRemover Cleanup(TempPath);
+
+ // Make sure it exists.
+ ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));
+
+ ASSERT_NO_ERROR(fs::is_local(FD, Result));
+ EXPECT_TRUE(Result);
+ EXPECT_TRUE(fs::is_local(FD));
+}
+
TEST_F(FileSystemTest, set_current_path) {
SmallString<128> path;
More information about the llvm-commits
mailing list