in lib/LLVMSupport/Support/Unix/Path.inc [384:1030]
static bool is_local_impl(struct STATVFS &Vfs) {
#if defined(__linux__) || defined(__GNU__)
#ifndef NFS_SUPER_MAGIC
#define NFS_SUPER_MAGIC 0x6969
#endif
#ifndef SMB_SUPER_MAGIC
#define SMB_SUPER_MAGIC 0x517B
#endif
#ifndef CIFS_MAGIC_NUMBER
#define CIFS_MAGIC_NUMBER 0xFF534D42
#endif
#ifdef __GNU__
switch ((uint32_t)Vfs.__f_type) {
#else
switch ((uint32_t)Vfs.f_type) {
#endif
case NFS_SUPER_MAGIC:
case SMB_SUPER_MAGIC:
case CIFS_MAGIC_NUMBER:
return false;
default:
return true;
}
#elif defined(__CYGWIN__)
// Cygwin doesn't expose this information; would need to use Win32 API.
return false;
#elif defined(__Fuchsia__)
// Fuchsia doesn't yet support remote filesystem mounts.
return true;
#elif defined(__HAIKU__)
// Haiku doesn't expose this information.
return false;
#elif defined(__sun)
// statvfs::f_basetype contains a null-terminated FSType name of the mounted target
StringRef fstype(Vfs.f_basetype);
// NFS is the only non-local fstype??
return !fstype.equals("nfs");
#elif defined(_AIX)
// Call mntctl; try more than twice in case of timing issues with a concurrent
// mount.
int Ret;
size_t BufSize = 2048u;
std::unique_ptr<char[]> Buf;
int Tries = 3;
while (Tries--) {
Buf = llvm::make_unique<char[]>(BufSize);
Ret = mntctl(MCTL_QUERY, BufSize, Buf.get());
if (Ret != 0)
break;
BufSize = *reinterpret_cast<unsigned int *>(Buf.get());
Buf.reset();
}
if (Ret == -1)
// There was an error; "remote" is the conservative answer.
return false;
// Look for the correct vmount entry.
char *CurObjPtr = Buf.get();
while (Ret--) {
struct vmount *Vp = reinterpret_cast<struct vmount *>(CurObjPtr);
static_assert(sizeof(Vfs.f_fsid) == sizeof(Vp->vmt_fsid),
"fsid length mismatch");
if (memcmp(&Vfs.f_fsid, &Vp->vmt_fsid, sizeof Vfs.f_fsid) == 0)
return (Vp->vmt_flags & MNT_REMOTE) == 0;
CurObjPtr += Vp->vmt_length;
}
// vmount entry not found; "remote" is the conservative answer.
return false;
#else
return !!(STATVFS_F_FLAG(Vfs) & MNT_LOCAL);
#endif
}
std::error_code is_local(const Twine &Path, bool &Result) {
struct STATVFS Vfs;
if (::STATVFS(const_cast<char *>(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;
SmallString<128> to_storage;
StringRef f = from.toNullTerminatedStringRef(from_storage);
StringRef t = to.toNullTerminatedStringRef(to_storage);
if (::rename(f.begin(), t.begin()) == -1)
return std::error_code(errno, std::generic_category());
return std::error_code();
}
std::error_code resize_file(int FD, uint64_t Size) {
#if defined(HAVE_POSIX_FALLOCATE)
// If we have posix_fallocate use it. Unlike ftruncate it always allocates
// space, so we get an error if the disk is full.
if (int Err = ::posix_fallocate(FD, 0, Size)) {
#ifdef _AIX
constexpr int NotSupportedError = ENOTSUP;
#else
constexpr int NotSupportedError = EOPNOTSUPP;
#endif
if (Err != EINVAL && Err != NotSupportedError)
return std::error_code(Err, std::generic_category());
}
#endif
// Use ftruncate as a fallback. It may or may not allocate space. At least on
// OS X with HFS+ it does.
if (::ftruncate(FD, Size) == -1)
return std::error_code(errno, std::generic_category());
return std::error_code();
}
static int convertAccessMode(AccessMode Mode) {
switch (Mode) {
case AccessMode::Exist:
return F_OK;
case AccessMode::Write:
return W_OK;
case AccessMode::Execute:
return R_OK | X_OK; // scripts also need R_OK.
}
llvm_unreachable("invalid enum");
}
std::error_code access(const Twine &Path, AccessMode Mode) {
SmallString<128> PathStorage;
StringRef P = Path.toNullTerminatedStringRef(PathStorage);
if (::access(P.begin(), convertAccessMode(Mode)) == -1)
return std::error_code(errno, std::generic_category());
if (Mode == AccessMode::Execute) {
// Don't say that directories are executable.
struct stat buf;
if (0 != stat(P.begin(), &buf))
return errc::permission_denied;
if (!S_ISREG(buf.st_mode))
return errc::permission_denied;
}
return std::error_code();
}
bool can_execute(const Twine &Path) {
return !access(Path, AccessMode::Execute);
}
bool equivalent(file_status A, file_status B) {
assert(status_known(A) && status_known(B));
return A.fs_st_dev == B.fs_st_dev &&
A.fs_st_ino == B.fs_st_ino;
}
std::error_code equivalent(const Twine &A, const Twine &B, bool &result) {
file_status fsA, fsB;
if (std::error_code ec = status(A, fsA))
return ec;
if (std::error_code ec = status(B, fsB))
return ec;
result = equivalent(fsA, fsB);
return std::error_code();
}
static void expandTildeExpr(SmallVectorImpl<char> &Path) {
StringRef PathStr(Path.begin(), Path.size());
if (PathStr.empty() || !PathStr.startswith("~"))
return;
PathStr = PathStr.drop_front();
StringRef Expr =
PathStr.take_until([](char c) { return path::is_separator(c); });
StringRef Remainder = PathStr.substr(Expr.size() + 1);
SmallString<128> Storage;
if (Expr.empty()) {
// This is just ~/..., resolve it to the current user's home dir.
if (!path::home_directory(Storage)) {
// For some reason we couldn't get the home directory. Just exit.
return;
}
// Overwrite the first character and insert the rest.
Path[0] = Storage[0];
Path.insert(Path.begin() + 1, Storage.begin() + 1, Storage.end());
return;
}
// This is a string of the form ~username/, look up this user's entry in the
// password database.
struct passwd *Entry = nullptr;
std::string User = Expr.str();
Entry = ::getpwnam(User.c_str());
if (!Entry) {
// Unable to look up the entry, just return back the original path.
return;
}
Storage = Remainder;
Path.clear();
Path.append(Entry->pw_dir, Entry->pw_dir + strlen(Entry->pw_dir));
llvm::sys::path::append(Path, Storage);
}
void expand_tilde(const Twine &path, SmallVectorImpl<char> &dest) {
dest.clear();
if (path.isTriviallyEmpty())
return;
path.toVector(dest);
expandTildeExpr(dest);
return;
}
static file_type typeForMode(mode_t Mode) {
if (S_ISDIR(Mode))
return file_type::directory_file;
else if (S_ISREG(Mode))
return file_type::regular_file;
else if (S_ISBLK(Mode))
return file_type::block_file;
else if (S_ISCHR(Mode))
return file_type::character_file;
else if (S_ISFIFO(Mode))
return file_type::fifo_file;
else if (S_ISSOCK(Mode))
return file_type::socket_file;
else if (S_ISLNK(Mode))
return file_type::symlink_file;
return file_type::type_unknown;
}
static std::error_code fillStatus(int StatRet, const struct stat &Status,
file_status &Result) {
if (StatRet != 0) {
std::error_code EC(errno, std::generic_category());
if (EC == errc::no_such_file_or_directory)
Result = file_status(file_type::file_not_found);
else
Result = file_status(file_type::status_error);
return EC;
}
uint32_t atime_nsec, mtime_nsec;
#if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC)
atime_nsec = Status.st_atimespec.tv_nsec;
mtime_nsec = Status.st_mtimespec.tv_nsec;
#elif defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
atime_nsec = Status.st_atim.tv_nsec;
mtime_nsec = Status.st_mtim.tv_nsec;
#else
atime_nsec = mtime_nsec = 0;
#endif
perms Perms = static_cast<perms>(Status.st_mode) & all_perms;
Result = file_status(typeForMode(Status.st_mode), Perms, Status.st_dev,
Status.st_nlink, Status.st_ino,
Status.st_atime, atime_nsec, Status.st_mtime, mtime_nsec,
Status.st_uid, Status.st_gid, Status.st_size);
return std::error_code();
}
std::error_code status(const Twine &Path, file_status &Result, bool Follow) {
SmallString<128> PathStorage;
StringRef P = Path.toNullTerminatedStringRef(PathStorage);
struct stat Status;
int StatRet = (Follow ? ::stat : ::lstat)(P.begin(), &Status);
return fillStatus(StatRet, Status, Result);
}
std::error_code status(int FD, file_status &Result) {
struct stat Status;
int StatRet = ::fstat(FD, &Status);
return fillStatus(StatRet, Status, Result);
}
std::error_code setPermissions(const Twine &Path, perms Permissions) {
SmallString<128> PathStorage;
StringRef P = Path.toNullTerminatedStringRef(PathStorage);
if (::chmod(P.begin(), Permissions))
return std::error_code(errno, std::generic_category());
return std::error_code();
}
std::error_code setLastAccessAndModificationTime(int FD, TimePoint<> AccessTime,
TimePoint<> ModificationTime) {
#if defined(HAVE_FUTIMENS)
timespec Times[2];
Times[0] = sys::toTimeSpec(AccessTime);
Times[1] = sys::toTimeSpec(ModificationTime);
if (::futimens(FD, Times))
return std::error_code(errno, std::generic_category());
return std::error_code();
#elif defined(HAVE_FUTIMES)
timeval Times[2];
Times[0] = sys::toTimeVal(
std::chrono::time_point_cast<std::chrono::microseconds>(AccessTime));
Times[1] =
sys::toTimeVal(std::chrono::time_point_cast<std::chrono::microseconds>(
ModificationTime));
if (::futimes(FD, Times))
return std::error_code(errno, std::generic_category());
return std::error_code();
#else
#warning Missing futimes() and futimens()
return make_error_code(errc::function_not_supported);
#endif
}
std::error_code mapped_file_region::init(int FD, uint64_t Offset,
mapmode Mode) {
assert(Size != 0);
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. These flags are only usable when mapping
// with PROT_READ, so take care not to specify them otherwise.
//----------------------------------------------------------------------
if (Mode == readonly) {
#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());
return std::error_code();
}
mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
uint64_t offset, std::error_code &ec)
: Size(length), Mapping(), Mode(mode) {
(void)Mode;
ec = init(fd, offset, mode);
if (ec)
Mapping = nullptr;
}
mapped_file_region::~mapped_file_region() {
if (Mapping)
::munmap(Mapping, Size);
}
size_t mapped_file_region::size() const {
assert(Mapping && "Mapping failed but used anyway!");
return Size;
}
char *mapped_file_region::data() const {
assert(Mapping && "Mapping failed but used anyway!");
return reinterpret_cast<char*>(Mapping);
}
const char *mapped_file_region::const_data() const {
assert(Mapping && "Mapping failed but used anyway!");
return reinterpret_cast<const char*>(Mapping);
}
int mapped_file_region::alignment() {
return Process::getPageSize();
}
std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
StringRef path,
bool follow_symlinks) {
SmallString<128> path_null(path);
DIR *directory = ::opendir(path_null.c_str());
if (!directory)
return std::error_code(errno, std::generic_category());
it.IterationHandle = reinterpret_cast<intptr_t>(directory);
// Add something for replace_filename to replace.
path::append(path_null, ".");
it.CurrentEntry = directory_entry(path_null.str(), follow_symlinks);
return directory_iterator_increment(it);
}
std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) {
if (it.IterationHandle)
::closedir(reinterpret_cast<DIR *>(it.IterationHandle));
it.IterationHandle = 0;
it.CurrentEntry = directory_entry();
return std::error_code();
}
static file_type direntType(dirent* Entry) {
// Most platforms provide the file type in the dirent: Linux/BSD/Mac.
// The DTTOIF macro lets us reuse our status -> type conversion.
#if defined(_DIRENT_HAVE_D_TYPE) && defined(DTTOIF)
return typeForMode(DTTOIF(Entry->d_type));
#else
// Other platforms such as Solaris require a stat() to get the type.
return file_type::type_unknown;
#endif
}
std::error_code detail::directory_iterator_increment(detail::DirIterState &It) {
errno = 0;
dirent *CurDir = ::readdir(reinterpret_cast<DIR *>(It.IterationHandle));
if (CurDir == nullptr && errno != 0) {
return std::error_code(errno, std::generic_category());
} else if (CurDir != nullptr) {
StringRef Name(CurDir->d_name);
if ((Name.size() == 1 && Name[0] == '.') ||
(Name.size() == 2 && Name[0] == '.' && Name[1] == '.'))
return directory_iterator_increment(It);
It.CurrentEntry.replace_filename(Name, direntType(CurDir));
} else
return directory_iterator_destruct(It);
return std::error_code();
}
ErrorOr<basic_file_status> directory_entry::status() const {
file_status s;
if (auto EC = fs::status(Path, s, FollowSymlinks))
return EC;
return s;
}
#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
static int nativeOpenFlags(CreationDisposition Disp, OpenFlags Flags,
FileAccess Access) {
int Result = 0;
if (Access == FA_Read)
Result |= O_RDONLY;
else if (Access == FA_Write)
Result |= O_WRONLY;
else if (Access == (FA_Read | FA_Write))
Result |= O_RDWR;
// This is for compatibility with old code that assumed F_Append implied
// would open an existing file. See Windows/Path.inc for a longer comment.
if (Flags & F_Append)
Disp = CD_OpenAlways;
if (Disp == CD_CreateNew) {
Result |= O_CREAT; // Create if it doesn't exist.
Result |= O_EXCL; // Fail if it does.
} else if (Disp == CD_CreateAlways) {
Result |= O_CREAT; // Create if it doesn't exist.
Result |= O_TRUNC; // Truncate if it does.
} else if (Disp == CD_OpenAlways) {
Result |= O_CREAT; // Create if it doesn't exist.
} else if (Disp == CD_OpenExisting) {
// Nothing special, just don't add O_CREAT and we get these semantics.
}
if (Flags & F_Append)
Result |= O_APPEND;
#ifdef O_CLOEXEC
if (!(Flags & OF_ChildInherit))
Result |= O_CLOEXEC;
#endif
return Result;
}
std::error_code openFile(const Twine &Name, int &ResultFD,
CreationDisposition Disp, FileAccess Access,
OpenFlags Flags, unsigned Mode) {
int OpenFlags = nativeOpenFlags(Disp, Flags, Access);
SmallString<128> Storage;
StringRef P = Name.toNullTerminatedStringRef(Storage);
// Call ::open in a lambda to avoid overload resolution in RetryAfterSignal
// when open is overloaded, such as in Bionic.
auto Open = [&]() { return ::open(P.begin(), OpenFlags, Mode); };
if ((ResultFD = sys::RetryAfterSignal(-1, Open)) < 0)
return std::error_code(errno, std::generic_category());
#ifndef O_CLOEXEC
if (!(Flags & OF_ChildInherit)) {
int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC);
(void)r;
assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed");
}
#endif
return std::error_code();
}
Expected<int> openNativeFile(const Twine &Name, CreationDisposition Disp,
FileAccess Access, OpenFlags Flags,
unsigned Mode) {
int FD;
std::error_code EC = openFile(Name, FD, Disp, Access, Flags, Mode);
if (EC)
return errorCodeToError(EC);
return FD;
}
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
OpenFlags Flags,
SmallVectorImpl<char> *RealPath) {
std::error_code EC =
openFile(Name, ResultFD, CD_OpenExisting, FA_Read, Flags, 0666);
if (EC)
return EC;
// 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 {
SmallString<128> Storage;
StringRef P = Name.toNullTerminatedStringRef(Storage);
// 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();
}
Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags,
SmallVectorImpl<char> *RealPath) {
file_t ResultFD;
std::error_code EC = openFileForRead(Name, ResultFD, Flags, RealPath);
if (EC)
return errorCodeToError(EC);
return ResultFD;
}
void closeFile(file_t &F) {
::close(F);
F = kInvalidFile;
}
template <typename T>
static std::error_code remove_directories_impl(const T &Entry,
bool IgnoreErrors) {
std::error_code EC;
directory_iterator Begin(Entry, EC, false);
directory_iterator End;
while (Begin != End) {
auto &Item = *Begin;
ErrorOr<basic_file_status> st = Item.status();
if (!st && !IgnoreErrors)
return st.getError();
if (is_directory(*st)) {
EC = remove_directories_impl(Item, IgnoreErrors);
if (EC && !IgnoreErrors)
return EC;
}
EC = fs::remove(Item.path(), true);
if (EC && !IgnoreErrors)
return EC;
Begin.increment(EC);
if (EC && !IgnoreErrors)
return EC;
}
return std::error_code();
}
std::error_code remove_directories(const Twine &path, bool IgnoreErrors) {
auto EC = remove_directories_impl(path, IgnoreErrors);
if (EC && !IgnoreErrors)
return EC;
EC = fs::remove(path, true);
if (EC && !IgnoreErrors)
return EC;
return std::error_code();
}
std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest,
bool expand_tilde) {
dest.clear();
if (path.isTriviallyEmpty())
return std::error_code();
if (expand_tilde) {
SmallString<128> Storage;
path.toVector(Storage);
expandTildeExpr(Storage);
return real_path(Storage, dest, false);
}
SmallString<128> Storage;
StringRef P = path.toNullTerminatedStringRef(Storage);
char Buffer[PATH_MAX];
if (::realpath(P.begin(), Buffer) == nullptr)
return std::error_code(errno, std::generic_category());
dest.append(Buffer, Buffer + strlen(Buffer));
return std::error_code();
}
} // end namespace fs