Skip to content

Commit

Permalink
[Support] Add a function to check if a file resides locally.
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D30010

llvm-svn: 295768
  • Loading branch information
Zachary Turner committed Feb 21, 2017
1 parent ccee0e0 commit 392ed9d
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 31 deletions.
26 changes: 26 additions & 0 deletions llvm/include/llvm/Support/FileSystem.h
Expand Up @@ -464,6 +464,32 @@ inline bool equivalent(const Twine &A, const Twine &B) {
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.
Expand Down
20 changes: 10 additions & 10 deletions llvm/include/llvm/Support/MemoryBuffer.h
Expand Up @@ -69,12 +69,12 @@ class MemoryBuffer {
/// 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
Expand All @@ -87,17 +87,17 @@ class MemoryBuffer {
/// 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.
Expand Down Expand Up @@ -136,7 +136,7 @@ class MemoryBuffer {

/// 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.
Expand Down
34 changes: 16 additions & 18 deletions llvm/lib/Support/MemoryBuffer.cpp
Expand Up @@ -103,7 +103,7 @@ class MemoryBufferMem : public MemoryBuffer {

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,
Expand Down Expand Up @@ -178,8 +178,8 @@ MemoryBuffer::getFileOrSTDIN(const Twine &Filename, int64_t FileSize,

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);
}


Expand Down Expand Up @@ -254,27 +254,27 @@ getMemoryBufferForStream(int FD, const Twine &BufferName) {

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)
return EC;

ErrorOr<std::unique_ptr<MemoryBuffer>> Ret =
getOpenFileImpl(FD, Filename, FileSize, MapSize, Offset,
RequiresNullTerminator, IsVolatileSize);
RequiresNullTerminator, IsVolatile);
close(FD);
return Ret;
}
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -365,7 +364,7 @@ getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize,
}

if (shouldUseMmap(FD, FileSize, MapSize, Offset, RequiresNullTerminator,
PageSize, IsVolatileSize)) {
PageSize, IsVolatile)) {
std::error_code EC;
std::unique_ptr<MemoryBuffer> Result(
new (NamedBufferAlloc(Filename))
Expand Down Expand Up @@ -415,17 +414,16 @@ getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize,

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() {
Expand Down
66 changes: 63 additions & 3 deletions llvm/lib/Support/Unix/Path.inc
Expand Up @@ -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;

Expand Down Expand Up @@ -335,6 +344,40 @@ std::error_code remove(const Twine &path, bool IgnoreNonExisting) {
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;
Expand Down Expand Up @@ -491,6 +534,23 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset,

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());
Expand Down
74 changes: 74 additions & 0 deletions llvm/lib/Support/Windows/Path.inc
Expand Up @@ -277,6 +277,80 @@ std::error_code remove(const Twine &path, bool IgnoreNonExisting) {
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;
Expand Down

0 comments on commit 392ed9d

Please sign in to comment.