mirror of
https://github.com/RPCSX/llvm.git
synced 2025-02-01 18:12:49 +00:00
[Support] Add llvm::sys::fs::remove_directories.
We already have a function create_directories() which can create an entire tree, and remove() which can remove an empty directory, but we do not have remove_directories() which can remove an entire tree. This patch adds such a function. Because removing a directory tree can have dangerous consequences when the tree contains a directory symlink, the patch here updates the existing directory_iterator construct to optionally not follow symlinks (previously it would always follow symlinks). The delete algorithm uses this flag so that for symlinks, only the links are removed, and not the targets. On Windows this is implemented with SHFileOperation, which also does not recurse into symbolic links or junctions. Differential Revision: https://reviews.llvm.org/D30676 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@297314 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
7b285dee52
commit
c036eebbeb
@ -361,6 +361,13 @@ std::error_code set_current_path(const Twine &path);
|
||||
/// returns error if the file didn't exist.
|
||||
std::error_code remove(const Twine &path, bool IgnoreNonExisting = true);
|
||||
|
||||
/// @brief Recursively delete a directory.
|
||||
///
|
||||
/// @param path Input path.
|
||||
/// @returns errc::success if path has been removed or didn't exist, otherwise a
|
||||
/// platform-specific error code.
|
||||
std::error_code remove_directories(const Twine &path, bool IgnoreErrors = true);
|
||||
|
||||
/// @brief Rename \a from to \a to. Files are renamed as if by POSIX rename().
|
||||
///
|
||||
/// @param from The path to rename from.
|
||||
@ -792,12 +799,13 @@ std::string getMainExecutable(const char *argv0, void *MainExecAddr);
|
||||
/// called.
|
||||
class directory_entry {
|
||||
std::string Path;
|
||||
bool FollowSymlinks;
|
||||
mutable file_status Status;
|
||||
|
||||
public:
|
||||
explicit directory_entry(const Twine &path, file_status st = file_status())
|
||||
: Path(path.str())
|
||||
, Status(st) {}
|
||||
explicit directory_entry(const Twine &path, bool follow_symlinks = true,
|
||||
file_status st = file_status())
|
||||
: Path(path.str()), FollowSymlinks(follow_symlinks), Status(st) {}
|
||||
|
||||
directory_entry() = default;
|
||||
|
||||
@ -823,7 +831,7 @@ namespace detail {
|
||||
|
||||
struct DirIterState;
|
||||
|
||||
std::error_code directory_iterator_construct(DirIterState &, StringRef);
|
||||
std::error_code directory_iterator_construct(DirIterState &, StringRef, bool);
|
||||
std::error_code directory_iterator_increment(DirIterState &);
|
||||
std::error_code directory_iterator_destruct(DirIterState &);
|
||||
|
||||
@ -844,18 +852,24 @@ namespace detail {
|
||||
/// it call report_fatal_error on error.
|
||||
class directory_iterator {
|
||||
std::shared_ptr<detail::DirIterState> State;
|
||||
bool FollowSymlinks = true;
|
||||
|
||||
public:
|
||||
explicit directory_iterator(const Twine &path, std::error_code &ec) {
|
||||
explicit directory_iterator(const Twine &path, std::error_code &ec,
|
||||
bool follow_symlinks = true)
|
||||
: FollowSymlinks(follow_symlinks) {
|
||||
State = std::make_shared<detail::DirIterState>();
|
||||
SmallString<128> path_storage;
|
||||
ec = detail::directory_iterator_construct(*State,
|
||||
path.toStringRef(path_storage));
|
||||
ec = detail::directory_iterator_construct(
|
||||
*State, path.toStringRef(path_storage), FollowSymlinks);
|
||||
}
|
||||
|
||||
explicit directory_iterator(const directory_entry &de, std::error_code &ec) {
|
||||
explicit directory_iterator(const directory_entry &de, std::error_code &ec,
|
||||
bool follow_symlinks = true)
|
||||
: FollowSymlinks(follow_symlinks) {
|
||||
State = std::make_shared<detail::DirIterState>();
|
||||
ec = detail::directory_iterator_construct(*State, de.path());
|
||||
ec =
|
||||
detail::directory_iterator_construct(*State, de.path(), FollowSymlinks);
|
||||
}
|
||||
|
||||
/// Construct end iterator.
|
||||
@ -902,12 +916,15 @@ namespace detail {
|
||||
/// recurses down into child directories.
|
||||
class recursive_directory_iterator {
|
||||
std::shared_ptr<detail::RecDirIterState> State;
|
||||
bool Follow;
|
||||
|
||||
public:
|
||||
recursive_directory_iterator() = default;
|
||||
explicit recursive_directory_iterator(const Twine &path, std::error_code &ec)
|
||||
: State(std::make_shared<detail::RecDirIterState>()) {
|
||||
State->Stack.push(directory_iterator(path, ec));
|
||||
explicit recursive_directory_iterator(const Twine &path, std::error_code &ec,
|
||||
bool follow_symlinks = true)
|
||||
: State(std::make_shared<detail::RecDirIterState>()),
|
||||
Follow(follow_symlinks) {
|
||||
State->Stack.push(directory_iterator(path, ec, Follow));
|
||||
if (State->Stack.top() == directory_iterator())
|
||||
State.reset();
|
||||
}
|
||||
@ -922,7 +939,7 @@ public:
|
||||
file_status st;
|
||||
if ((ec = State->Stack.top()->status(st))) return *this;
|
||||
if (is_directory(st)) {
|
||||
State->Stack.push(directory_iterator(*State->Stack.top(), ec));
|
||||
State->Stack.push(directory_iterator(*State->Stack.top(), ec, Follow));
|
||||
if (ec) return *this;
|
||||
if (State->Stack.top() != end_itr) {
|
||||
++State->Level;
|
||||
|
@ -1189,7 +1189,7 @@ std::error_code identify_magic(const Twine &Path, file_magic &Result) {
|
||||
}
|
||||
|
||||
std::error_code directory_entry::status(file_status &result) const {
|
||||
return fs::status(Path, result);
|
||||
return fs::status(Path, result, FollowSymlinks);
|
||||
}
|
||||
|
||||
} // end namespace fs
|
||||
|
@ -618,7 +618,8 @@ int mapped_file_region::alignment() {
|
||||
}
|
||||
|
||||
std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
|
||||
StringRef path){
|
||||
StringRef path,
|
||||
bool follow_symlinks) {
|
||||
SmallString<128> path_null(path);
|
||||
DIR *directory = ::opendir(path_null.c_str());
|
||||
if (!directory)
|
||||
@ -627,7 +628,7 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
|
||||
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());
|
||||
it.CurrentEntry = directory_entry(path_null.str(), follow_symlinks);
|
||||
return directory_iterator_increment(it);
|
||||
}
|
||||
|
||||
@ -795,6 +796,46 @@ std::error_code getPathFromOpenFD(int FD, SmallVectorImpl<char> &ResultPath) {
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
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;
|
||||
file_status st;
|
||||
EC = Item.status(st);
|
||||
if (EC && !IgnoreErrors)
|
||||
return EC;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
} // end namespace fs
|
||||
|
||||
namespace path {
|
||||
|
@ -26,6 +26,7 @@
|
||||
// These two headers must be included last, and make sure shlobj is required
|
||||
// after Windows.h to make sure it picks up our definition of _WIN32_WINNT
|
||||
#include "WindowsSupport.h"
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h>
|
||||
|
||||
#undef max
|
||||
@ -693,7 +694,8 @@ int mapped_file_region::alignment() {
|
||||
}
|
||||
|
||||
std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
|
||||
StringRef path){
|
||||
StringRef path,
|
||||
bool follow_symlinks) {
|
||||
SmallVector<wchar_t, 128> path_utf16;
|
||||
|
||||
if (std::error_code ec = widenPath(path, path_utf16))
|
||||
@ -738,7 +740,7 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
|
||||
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);
|
||||
it.CurrentEntry = directory_entry(directory_entry_path, follow_symlinks);
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
@ -920,6 +922,33 @@ std::error_code getPathFromOpenFD(int FD, SmallVectorImpl<char> &ResultPath) {
|
||||
|
||||
return windows::UTF16ToUTF8(TempPath.data(), CharCount, ResultPath);
|
||||
}
|
||||
|
||||
std::error_code remove_directories(const Twine &path, bool IgnoreErrors) {
|
||||
// Convert to utf-16.
|
||||
SmallVector<wchar_t, 128> Path16;
|
||||
std::error_code EC = widenPath(path, Path16);
|
||||
if (EC && !IgnoreErrors)
|
||||
return EC;
|
||||
|
||||
// SHFileOperation() accepts a list of paths, and so must be double null-
|
||||
// terminated to indicate the end of the list. The buffer is already null
|
||||
// terminated, but since that null character is not considered part of the
|
||||
// vector's size, pushing another one will just consume that byte. So we
|
||||
// need to push 2 null terminators.
|
||||
Path16.push_back(0);
|
||||
Path16.push_back(0);
|
||||
|
||||
SHFILEOPSTRUCTW shfos = {};
|
||||
shfos.wFunc = FO_DELETE;
|
||||
shfos.pFrom = Path16.data();
|
||||
shfos.fFlags = FOF_NO_UI;
|
||||
|
||||
int result = ::SHFileOperationW(&shfos);
|
||||
if (result != 0 && !IgnoreErrors)
|
||||
return mapWindowsError(result);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
} // end namespace fs
|
||||
|
||||
namespace path {
|
||||
|
@ -743,6 +743,39 @@ TEST_F(FileSystemTest, DirectoryIteration) {
|
||||
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel"));
|
||||
}
|
||||
|
||||
TEST_F(FileSystemTest, Remove) {
|
||||
SmallString<64> BaseDir;
|
||||
SmallString<64> Paths[4];
|
||||
int fds[4];
|
||||
ASSERT_NO_ERROR(fs::createUniqueDirectory("fs_remove", BaseDir));
|
||||
|
||||
ASSERT_NO_ERROR(fs::create_directories(Twine(BaseDir) + "/foo/bar/baz"));
|
||||
ASSERT_NO_ERROR(fs::create_directories(Twine(BaseDir) + "/foo/bar/buzz"));
|
||||
ASSERT_NO_ERROR(fs::createUniqueFile(
|
||||
Twine(BaseDir) + "/foo/bar/baz/%%%%%%.tmp", fds[0], Paths[0]));
|
||||
ASSERT_NO_ERROR(fs::createUniqueFile(
|
||||
Twine(BaseDir) + "/foo/bar/baz/%%%%%%.tmp", fds[1], Paths[1]));
|
||||
ASSERT_NO_ERROR(fs::createUniqueFile(
|
||||
Twine(BaseDir) + "/foo/bar/buzz/%%%%%%.tmp", fds[2], Paths[2]));
|
||||
ASSERT_NO_ERROR(fs::createUniqueFile(
|
||||
Twine(BaseDir) + "/foo/bar/buzz/%%%%%%.tmp", fds[3], Paths[3]));
|
||||
|
||||
for (int fd : fds)
|
||||
::close(fd);
|
||||
|
||||
EXPECT_TRUE(fs::exists(Twine(BaseDir) + "/foo/bar/baz"));
|
||||
EXPECT_TRUE(fs::exists(Twine(BaseDir) + "/foo/bar/buzz"));
|
||||
EXPECT_TRUE(fs::exists(Paths[0]));
|
||||
EXPECT_TRUE(fs::exists(Paths[1]));
|
||||
EXPECT_TRUE(fs::exists(Paths[2]));
|
||||
EXPECT_TRUE(fs::exists(Paths[3]));
|
||||
|
||||
ASSERT_NO_ERROR(fs::remove_directories("D:/footest"));
|
||||
|
||||
ASSERT_NO_ERROR(fs::remove_directories(BaseDir));
|
||||
ASSERT_FALSE(fs::exists(BaseDir));
|
||||
}
|
||||
|
||||
const char archive[] = "!<arch>\x0A";
|
||||
const char bitcode[] = "\xde\xc0\x17\x0b";
|
||||
const char coff_object[] = "\x00\x00......";
|
||||
|
Loading…
x
Reference in New Issue
Block a user