[libcxx-commits] [libcxx] c31455c - [libc++] Fix filesystem::remove_all bug with read-only nested directory (#197104)

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jul 13 06:22:39 PDT 2026


Author: Jake Egan
Date: 2026-07-13T09:22:34-04:00
New Revision: c31455cd07c43ed02445f5b1f1057037f0503552

URL: https://github.com/llvm/llvm-project/commit/c31455cd07c43ed02445f5b1f1057037f0503552
DIFF: https://github.com/llvm/llvm-project/commit/c31455cd07c43ed02445f5b1f1057037f0503552.diff

LOG: [libc++] Fix filesystem::remove_all bug with read-only nested directory (#197104)

Consider a directory `parent/child`, where `parent` has read-only
permissions. Calling `remove_all` on `parent` is expected to fail with a
permission error, however it fails with a different error depending on
the platform (such as "error file exists" on AIX).

During the recursive removal, `unlinkat` is first called on the child
and fails with a permission error. Rather than returning, the function
continues and tries `unlinkat` on the parent and fails with a different
error depending on the platform. This error is returned instead of the
permission error. This PR makes the first encountered error be returned
instead.

According to the standard [fs.op.remove.all]:
> Effects: Recursively deletes the contents of p if it exists, then
> deletes file p itself, as if by POSIX remove.

The word "then" implies an ordering where deleting the contents of p
should be successful before deleting file p itself.

Fixes #197100.

Added: 
    libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/bad_perms_parent.pass.cpp

Modified: 
    libcxx/src/filesystem/operations.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp
index 3d358dd55e8b8..cd55b797e96a2 100644
--- a/libcxx/src/filesystem/operations.cpp
+++ b/libcxx/src/filesystem/operations.cpp
@@ -905,6 +905,9 @@ uintmax_t remove_all_impl(int parent_directory, const path& p, error_code& ec) {
         break; // we're done iterating through the directory
       } else {
         count += remove_all_impl(fd, str, ec);
+        // If there's an error removing the child, return immediately to preserve the error code.
+        if (ec)
+          return count;
       }
     }
 

diff  --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/bad_perms_parent.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/bad_perms_parent.pass.cpp
new file mode 100644
index 0000000000000..c75c5f25dcce2
--- /dev/null
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/bad_perms_parent.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: no-filesystem
+
+// UNSUPPORTED: windows
+// XFAIL: using-built-library-before-llvm-23
+
+// Verify that remove_all reports the correct error (permission_denied)
+// when the parent directory has insufficient permissions.
+
+// <filesystem>
+
+#include <filesystem>
+
+#include "test_macros.h"
+#include "filesystem_test_helper.h"
+namespace fs = std::filesystem;
+
+int main(int, char**) {
+  scoped_test_env env;
+
+  const fs::path parent_dir = env.create_dir("parent");
+  const fs::path child_dir  = env.create_dir(parent_dir / "child");
+  permissions(parent_dir, fs::perms::owner_read | fs::perms::owner_write);
+
+  const auto BadRet = static_cast<std::uintmax_t>(-1);
+  std::error_code ec;
+  assert(fs::remove_all(parent_dir, ec) == BadRet);
+  assert(ec == std::errc::permission_denied);
+
+  permissions(parent_dir, fs::perms::owner_all);
+  return 0;
+}


        


More information about the libcxx-commits mailing list