mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 11:13:28 +01:00
[Support] sys::fs::directory_entry includes the file_type.
This is available on most platforms (Linux/Mac/Win/BSD) with no extra syscalls. On other platforms (e.g. Solaris) we stat() if this information is requested. This will allow switching clang's VFS to efficiently expose (path, type) when traversing a directory. Currently it exposes an entire Status, but does so by calling fs::status() on all platforms. Almost all callers only need the path, and all callers only need (path, type). Patch by sammccall (Sam McCall) Differential Revision: https://reviews.llvm.org/D51918 llvm-svn: 342089
This commit is contained in:
parent
5d6e7b38df
commit
a5be339044
@ -1100,38 +1100,51 @@ std::string getMainExecutable(const char *argv0, void *MainExecAddr);
|
||||
/// @name Iterators
|
||||
/// @{
|
||||
|
||||
/// directory_entry - A single entry in a directory. Caches the status either
|
||||
/// from the result of the iteration syscall, or the first time status is
|
||||
/// called.
|
||||
/// directory_entry - A single entry in a directory.
|
||||
class directory_entry {
|
||||
// FIXME: different platforms make different information available "for free"
|
||||
// when traversing a directory. The design of this class wraps most of the
|
||||
// information in basic_file_status, so on platforms where we can't populate
|
||||
// that whole structure, callers end up paying for a stat().
|
||||
// std::filesystem::directory_entry may be a better model.
|
||||
std::string Path;
|
||||
bool FollowSymlinks;
|
||||
basic_file_status Status;
|
||||
file_type Type; // Most platforms can provide this.
|
||||
bool FollowSymlinks; // Affects the behavior of status().
|
||||
basic_file_status Status; // If available.
|
||||
|
||||
public:
|
||||
explicit directory_entry(const Twine &path, bool follow_symlinks = true,
|
||||
basic_file_status st = basic_file_status())
|
||||
: Path(path.str()), FollowSymlinks(follow_symlinks), Status(st) {}
|
||||
explicit directory_entry(const Twine &Path, bool FollowSymlinks = true,
|
||||
file_type Type = file_type::type_unknown,
|
||||
basic_file_status Status = basic_file_status())
|
||||
: Path(Path.str()), Type(Type), FollowSymlinks(FollowSymlinks),
|
||||
Status(Status) {}
|
||||
|
||||
directory_entry() = default;
|
||||
|
||||
void assign(const Twine &path, basic_file_status st = basic_file_status()) {
|
||||
Path = path.str();
|
||||
Status = st;
|
||||
}
|
||||
|
||||
void replace_filename(const Twine &filename,
|
||||
basic_file_status st = basic_file_status());
|
||||
void replace_filename(const Twine &Filename, file_type Type,
|
||||
basic_file_status Status = basic_file_status());
|
||||
|
||||
const std::string &path() const { return Path; }
|
||||
// Get basic information about entry file (a subset of fs::status()).
|
||||
// On most platforms this is a stat() call.
|
||||
// On windows the information was already retrieved from the directory.
|
||||
ErrorOr<basic_file_status> status() const;
|
||||
// Get the type of this file.
|
||||
// On most platforms (Linux/Mac/Windows/BSD), this was already retrieved.
|
||||
// On some platforms (e.g. Solaris) this is a stat() call.
|
||||
file_type type() const {
|
||||
if (Type != file_type::type_unknown)
|
||||
return Type;
|
||||
auto S = status();
|
||||
return S ? S->type() : file_type::type_unknown;
|
||||
}
|
||||
|
||||
bool operator==(const directory_entry& rhs) const { return Path == rhs.Path; }
|
||||
bool operator!=(const directory_entry& rhs) const { return !(*this == rhs); }
|
||||
bool operator< (const directory_entry& rhs) const;
|
||||
bool operator<=(const directory_entry& rhs) const;
|
||||
bool operator> (const directory_entry& rhs) const;
|
||||
bool operator>=(const directory_entry& rhs) const;
|
||||
bool operator==(const directory_entry& RHS) const { return Path == RHS.Path; }
|
||||
bool operator!=(const directory_entry& RHS) const { return !(*this == RHS); }
|
||||
bool operator< (const directory_entry& RHS) const;
|
||||
bool operator<=(const directory_entry& RHS) const;
|
||||
bool operator> (const directory_entry& RHS) const;
|
||||
bool operator>=(const directory_entry& RHS) const;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
@ -1085,12 +1085,13 @@ std::error_code is_other(const Twine &Path, bool &Result) {
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
void directory_entry::replace_filename(const Twine &filename,
|
||||
basic_file_status st) {
|
||||
SmallString<128> path = path::parent_path(Path);
|
||||
path::append(path, filename);
|
||||
Path = path.str();
|
||||
Status = st;
|
||||
void directory_entry::replace_filename(const Twine &Filename, file_type Type,
|
||||
basic_file_status Status) {
|
||||
SmallString<128> PathStr = path::parent_path(Path);
|
||||
path::append(PathStr, Filename);
|
||||
this->Path = PathStr.str();
|
||||
this->Type = Type;
|
||||
this->Status = Status;
|
||||
}
|
||||
|
||||
ErrorOr<perms> getPermissions(const Twine &Path) {
|
||||
|
@ -523,38 +523,40 @@ static void expandTildeExpr(SmallVectorImpl<char> &Path) {
|
||||
llvm::sys::path::append(Path, Storage);
|
||||
}
|
||||
|
||||
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)
|
||||
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;
|
||||
return EC;
|
||||
}
|
||||
|
||||
file_type Type = file_type::type_unknown;
|
||||
|
||||
if (S_ISDIR(Status.st_mode))
|
||||
Type = file_type::directory_file;
|
||||
else if (S_ISREG(Status.st_mode))
|
||||
Type = file_type::regular_file;
|
||||
else if (S_ISBLK(Status.st_mode))
|
||||
Type = file_type::block_file;
|
||||
else if (S_ISCHR(Status.st_mode))
|
||||
Type = file_type::character_file;
|
||||
else if (S_ISFIFO(Status.st_mode))
|
||||
Type = file_type::fifo_file;
|
||||
else if (S_ISSOCK(Status.st_mode))
|
||||
Type = file_type::socket_file;
|
||||
else if (S_ISLNK(Status.st_mode))
|
||||
Type = file_type::symlink_file;
|
||||
|
||||
perms Perms = static_cast<perms>(Status.st_mode) & all_perms;
|
||||
Result = file_status(Type, Perms, Status.st_dev, Status.st_nlink,
|
||||
Status.st_ino, Status.st_atime, Status.st_mtime,
|
||||
Status.st_uid, Status.st_gid, Status.st_size);
|
||||
Result = file_status(typeForMode(Status.st_mode), Perms, Status.st_dev,
|
||||
Status.st_nlink, Status.st_ino, Status.st_atime,
|
||||
Status.st_mtime, Status.st_uid, Status.st_gid,
|
||||
Status.st_size);
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
@ -696,19 +698,30 @@ std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) {
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code detail::directory_iterator_increment(detail::DirIterState &it) {
|
||||
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 *cur_dir = ::readdir(reinterpret_cast<DIR *>(it.IterationHandle));
|
||||
if (cur_dir == nullptr && 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 (cur_dir != nullptr) {
|
||||
StringRef name(cur_dir->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);
|
||||
} 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 directory_iterator_destruct(It);
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
@ -902,28 +902,28 @@ static basic_file_status status_from_find_data(WIN32_FIND_DATAW *FindData) {
|
||||
FindData->nFileSizeHigh, FindData->nFileSizeLow);
|
||||
}
|
||||
|
||||
std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
|
||||
StringRef path,
|
||||
bool follow_symlinks) {
|
||||
SmallVector<wchar_t, 128> path_utf16;
|
||||
std::error_code detail::directory_iterator_construct(detail::DirIterState &IT,
|
||||
StringRef Path,
|
||||
bool FollowSymlinks) {
|
||||
SmallVector<wchar_t, 128> PathUTF16;
|
||||
|
||||
if (std::error_code ec = widenPath(path, path_utf16))
|
||||
return ec;
|
||||
if (std::error_code EC = widenPath(Path, PathUTF16))
|
||||
return EC;
|
||||
|
||||
// Convert path to the format that Windows is happy with.
|
||||
if (path_utf16.size() > 0 &&
|
||||
!is_separator(path_utf16[path.size() - 1]) &&
|
||||
path_utf16[path.size() - 1] != L':') {
|
||||
path_utf16.push_back(L'\\');
|
||||
path_utf16.push_back(L'*');
|
||||
if (PathUTF16.size() > 0 &&
|
||||
!is_separator(PathUTF16[Path.size() - 1]) &&
|
||||
PathUTF16[Path.size() - 1] != L':') {
|
||||
PathUTF16.push_back(L'\\');
|
||||
PathUTF16.push_back(L'*');
|
||||
} else {
|
||||
path_utf16.push_back(L'*');
|
||||
PathUTF16.push_back(L'*');
|
||||
}
|
||||
|
||||
// Get the first directory entry.
|
||||
WIN32_FIND_DATAW FirstFind;
|
||||
ScopedFindHandle FindHandle(::FindFirstFileExW(
|
||||
c_str(path_utf16), FindExInfoBasic, &FirstFind, FindExSearchNameMatch,
|
||||
c_str(PathUTF16), FindExInfoBasic, &FirstFind, FindExSearchNameMatch,
|
||||
NULL, FIND_FIRST_EX_LARGE_FETCH));
|
||||
if (!FindHandle)
|
||||
return mapWindowsError(::GetLastError());
|
||||
@ -936,43 +936,45 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
|
||||
DWORD LastError = ::GetLastError();
|
||||
// Check for end.
|
||||
if (LastError == ERROR_NO_MORE_FILES)
|
||||
return detail::directory_iterator_destruct(it);
|
||||
return detail::directory_iterator_destruct(IT);
|
||||
return mapWindowsError(LastError);
|
||||
} else
|
||||
FilenameLen = ::wcslen(FirstFind.cFileName);
|
||||
|
||||
// Construct the current directory entry.
|
||||
SmallString<128> directory_entry_name_utf8;
|
||||
if (std::error_code ec =
|
||||
SmallString<128> DirectoryEntryNameUTF8;
|
||||
if (std::error_code EC =
|
||||
UTF16ToUTF8(FirstFind.cFileName, ::wcslen(FirstFind.cFileName),
|
||||
directory_entry_name_utf8))
|
||||
return ec;
|
||||
DirectoryEntryNameUTF8))
|
||||
return EC;
|
||||
|
||||
it.IterationHandle = intptr_t(FindHandle.take());
|
||||
SmallString<128> directory_entry_path(path);
|
||||
path::append(directory_entry_path, directory_entry_name_utf8);
|
||||
it.CurrentEntry = directory_entry(directory_entry_path, follow_symlinks,
|
||||
status_from_find_data(&FirstFind));
|
||||
IT.IterationHandle = intptr_t(FindHandle.take());
|
||||
SmallString<128> DirectoryEntryPath(Path);
|
||||
path::append(DirectoryEntryPath, DirectoryEntryNameUTF8);
|
||||
IT.CurrentEntry =
|
||||
directory_entry(DirectoryEntryPath, FollowSymlinks,
|
||||
file_type_from_attrs(FirstFind.dwFileAttributes),
|
||||
status_from_find_data(&FirstFind));
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) {
|
||||
if (it.IterationHandle != 0)
|
||||
std::error_code detail::directory_iterator_destruct(detail::DirIterState &IT) {
|
||||
if (IT.IterationHandle != 0)
|
||||
// Closes the handle if it's valid.
|
||||
ScopedFindHandle close(HANDLE(it.IterationHandle));
|
||||
it.IterationHandle = 0;
|
||||
it.CurrentEntry = directory_entry();
|
||||
ScopedFindHandle close(HANDLE(IT.IterationHandle));
|
||||
IT.IterationHandle = 0;
|
||||
IT.CurrentEntry = directory_entry();
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code detail::directory_iterator_increment(detail::DirIterState &it) {
|
||||
std::error_code detail::directory_iterator_increment(detail::DirIterState &IT) {
|
||||
WIN32_FIND_DATAW FindData;
|
||||
if (!::FindNextFileW(HANDLE(it.IterationHandle), &FindData)) {
|
||||
if (!::FindNextFileW(HANDLE(IT.IterationHandle), &FindData)) {
|
||||
DWORD LastError = ::GetLastError();
|
||||
// Check for end.
|
||||
if (LastError == ERROR_NO_MORE_FILES)
|
||||
return detail::directory_iterator_destruct(it);
|
||||
return detail::directory_iterator_destruct(IT);
|
||||
return mapWindowsError(LastError);
|
||||
}
|
||||
|
||||
@ -980,16 +982,18 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &it) {
|
||||
if ((FilenameLen == 1 && FindData.cFileName[0] == L'.') ||
|
||||
(FilenameLen == 2 && FindData.cFileName[0] == L'.' &&
|
||||
FindData.cFileName[1] == L'.'))
|
||||
return directory_iterator_increment(it);
|
||||
return directory_iterator_increment(IT);
|
||||
|
||||
SmallString<128> directory_entry_path_utf8;
|
||||
if (std::error_code ec =
|
||||
SmallString<128> DirectoryEntryPathUTF8;
|
||||
if (std::error_code EC =
|
||||
UTF16ToUTF8(FindData.cFileName, ::wcslen(FindData.cFileName),
|
||||
directory_entry_path_utf8))
|
||||
return ec;
|
||||
DirectoryEntryPathUTF8))
|
||||
return EC;
|
||||
|
||||
it.CurrentEntry.replace_filename(Twine(directory_entry_path_utf8),
|
||||
status_from_find_data(&FindData));
|
||||
IT.CurrentEntry.replace_filename(
|
||||
Twine(DirectoryEntryPathUTF8),
|
||||
file_type_from_attrs(FindData.dwFileAttributes),
|
||||
status_from_find_data(&FindData));
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user