[libc-commits] [libc] [libc] Implement scandir and its unit tests (PR #223198)

Vadim Kotov via libc-commits libc-commits at lists.llvm.org
Wed Sep 16 11:55:14 PDT 2026


https://github.com/vadimkotov updated https://github.com/llvm/llvm-project/pull/223198

>From 4de87f420ff5aa0ad15ff8f4f851281dd368ffa1 Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Thu, 3 Sep 2026 15:17:45 -0700
Subject: [PATCH 01/15] Create boilerplate code for scandir and its unittest.

---
 libc/config/linux/x86_64/entrypoints.txt |  1 +
 libc/include/dirent.yaml                 |  9 +++++++
 libc/src/dirent/CMakeLists.txt           | 12 +++++++++
 libc/src/dirent/scandir.cpp              | 34 ++++++++++++++++++++++++
 libc/src/dirent/scandir.h                | 28 +++++++++++++++++++
 libc/test/src/dirent/CMakeLists.txt      | 11 ++++++++
 libc/test/src/dirent/scandir_test.cpp    | 21 +++++++++++++++
 7 files changed, 116 insertions(+)
 create mode 100644 libc/src/dirent/scandir.cpp
 create mode 100644 libc/src/dirent/scandir.h
 create mode 100644 libc/test/src/dirent/scandir_test.cpp

diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 42cbcd02513eb..5fb75675352cf 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1314,6 +1314,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.dirent.dirfd
     libc.src.dirent.opendir
     libc.src.dirent.readdir
+    libc.src.dirent.scandir
     libc.src.dirent.fdopendir
 
     # pthread.h entrypoints
diff --git a/libc/include/dirent.yaml b/libc/include/dirent.yaml
index e752b564d8d44..8dc939fa1158a 100644
--- a/libc/include/dirent.yaml
+++ b/libc/include/dirent.yaml
@@ -64,3 +64,12 @@ functions:
     return_type: struct dirent *
     arguments:
       - type: DIR *
+  - name: scandir
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: const char *
+      - type: struct dirent ***
+      - type: int (*)(const struct dirent *)
+      - type: int (*)(const struct dirent **, const struct dirent **)
diff --git a/libc/src/dirent/CMakeLists.txt b/libc/src/dirent/CMakeLists.txt
index d0ec22e1cc75b..8b8c09b661b32 100644
--- a/libc/src/dirent/CMakeLists.txt
+++ b/libc/src/dirent/CMakeLists.txt
@@ -49,6 +49,18 @@ add_entrypoint_object(
     libc.src.errno.errno
 )
 
+add_entrypoint_object(
+  scandir
+  SRCS
+    scandir.cpp
+  HDRS
+    scandir.h
+  DEPENDS
+    libc.hdr.types.struct_dirent
+    libc.src.__support.common
+    libc.src.__support.macros.config
+)
+
 add_entrypoint_object(
   fdopendir
   SRCS
diff --git a/libc/src/dirent/scandir.cpp b/libc/src/dirent/scandir.cpp
new file mode 100644
index 0000000000000..18b7779a3e260
--- /dev/null
+++ b/libc/src/dirent/scandir.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of getpriority.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/dirent/scandir.h"
+
+#include "hdr/types/struct_dirent.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, scandir,
+                   (const char *dir, struct dirent ***namelist,
+                    int (*sel)(const struct dirent *),
+                    int (*compar)(const struct dirent **,
+                                  const struct dirent **))) {
+  (void)dir;
+  (void)namelist;
+  (void)sel;
+  (void)compar;
+  return -1;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/dirent/scandir.h b/libc/src/dirent/scandir.h
new file mode 100644
index 0000000000000..03ffe076c781f
--- /dev/null
+++ b/libc/src/dirent/scandir.h
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header of scandir
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_DIRENT_SCANDIR_H
+#define LLVM_LIBC_SRC_DIRENT_SCANDIR_H
+
+#include "hdr/types/struct_dirent.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int scandir(const char *dir, struct dirent ***namelist,
+            int (*sel)(const struct dirent *),
+            int (*compar)(const struct dirent **, const struct dirent **));
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_DIRENT_SCANDIR_H
diff --git a/libc/test/src/dirent/CMakeLists.txt b/libc/test/src/dirent/CMakeLists.txt
index 1d7a3caf790dd..fd56b23d2c4ed 100644
--- a/libc/test/src/dirent/CMakeLists.txt
+++ b/libc/test/src/dirent/CMakeLists.txt
@@ -37,3 +37,14 @@ add_libc_test(
     libc.hdr.types.DIR
     libc.hdr.types.struct_dirent
 )
+
+add_libc_test(
+  scandir_test
+  SUITE
+    libc_dirent_unittests
+  SRCS
+    scandir_test.cpp
+  DEPENDS
+    libc.hdr.types.struct_dirent
+    libc.src.dirent.scandir
+)
diff --git a/libc/test/src/dirent/scandir_test.cpp b/libc/test/src/dirent/scandir_test.cpp
new file mode 100644
index 0000000000000..34e5e83c598a0
--- /dev/null
+++ b/libc/test/src/dirent/scandir_test.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for scandir.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/types/struct_dirent.h"
+#include "src/dirent/scandir.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcScandirTest, DummyTest) {
+  struct dirent **namelist;
+  ASSERT_NE(LIBC_NAMESPACE::scandir(".", &namelist, NULL, NULL), -1);
+}

>From cfce0c7ea992aabd77eb2133dc3556def7601c29 Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Tue, 8 Sep 2026 10:15:51 -0700
Subject: [PATCH 02/15] Fix a stray change after rebase

---
 libc/config/linux/x86_64/entrypoints.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 5fb75675352cf..a7654fea58ba7 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -614,7 +614,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.fenv.fesetexceptflag
     libc.src.fenv.fesetround
     libc.src.fenv.fetestexcept
-    libc.src.fenv.fetestexceptflag
+    libc.src.fenv.fegestexceptflag
     libc.src.fenv.feupdateenv
 
     # math.h entrypoints

>From f08f635087b15dbcd1ac47855166421926d34933 Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Tue, 8 Sep 2026 11:19:50 -0700
Subject: [PATCH 03/15] Mid-work commit due to a post-renase issue

---
 libc/src/dirent/CMakeLists.txt        |  1 +
 libc/src/dirent/scandir.cpp           | 12 ++++++++++--
 libc/test/src/dirent/CMakeLists.txt   |  2 ++
 libc/test/src/dirent/scandir_test.cpp |  9 +++++++--
 4 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/libc/src/dirent/CMakeLists.txt b/libc/src/dirent/CMakeLists.txt
index 8b8c09b661b32..4ad2a8b286a03 100644
--- a/libc/src/dirent/CMakeLists.txt
+++ b/libc/src/dirent/CMakeLists.txt
@@ -59,6 +59,7 @@ add_entrypoint_object(
     libc.hdr.types.struct_dirent
     libc.src.__support.common
     libc.src.__support.macros.config
+    libc.src.dirent.opendir
 )
 
 add_entrypoint_object(
diff --git a/libc/src/dirent/scandir.cpp b/libc/src/dirent/scandir.cpp
index 18b7779a3e260..45351d7aad034 100644
--- a/libc/src/dirent/scandir.cpp
+++ b/libc/src/dirent/scandir.cpp
@@ -16,6 +16,7 @@
 #include "hdr/types/struct_dirent.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
+#include "src/dirent/opendir.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
@@ -24,11 +25,18 @@ LLVM_LIBC_FUNCTION(int, scandir,
                     int (*sel)(const struct dirent *),
                     int (*compar)(const struct dirent **,
                                   const struct dirent **))) {
-  (void)dir;
   (void)namelist;
   (void)sel;
   (void)compar;
-  return -1;
+
+  DIR *dir_fd = opendir(dir);
+  if (dir_fd == nullptr) {
+    // opendir set errno
+    return -1;
+  }
+
+
+  return 0;
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/dirent/CMakeLists.txt b/libc/test/src/dirent/CMakeLists.txt
index fd56b23d2c4ed..309045f8a9536 100644
--- a/libc/test/src/dirent/CMakeLists.txt
+++ b/libc/test/src/dirent/CMakeLists.txt
@@ -47,4 +47,6 @@ add_libc_test(
   DEPENDS
     libc.hdr.types.struct_dirent
     libc.src.dirent.scandir
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
 )
diff --git a/libc/test/src/dirent/scandir_test.cpp b/libc/test/src/dirent/scandir_test.cpp
index 34e5e83c598a0..5be2c9c636bdb 100644
--- a/libc/test/src/dirent/scandir_test.cpp
+++ b/libc/test/src/dirent/scandir_test.cpp
@@ -13,9 +13,14 @@
 
 #include "hdr/types/struct_dirent.h"
 #include "src/dirent/scandir.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
 #include "test/UnitTest/Test.h"
 
-TEST(LlvmLibcScandirTest, DummyTest) {
+using LlvmLibcScandirTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+
+TEST_F(LlvmLibcScandirTest, TestBadDirname) {
   struct dirent **namelist;
-  ASSERT_NE(LIBC_NAMESPACE::scandir(".", &namelist, NULL, NULL), -1);
+  ASSERT_THAT(LIBC_NAMESPACE::scandir("", &namelist, NULL, NULL), Fails(ENOTDIR, -1));
 }

>From c683663013952e7b25f34ac0d47feae3a2dbd02c Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Tue, 8 Sep 2026 11:29:21 -0700
Subject: [PATCH 04/15] Fix a rebase typo

---
 libc/config/linux/x86_64/entrypoints.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index a7654fea58ba7..5fb75675352cf 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -614,7 +614,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.fenv.fesetexceptflag
     libc.src.fenv.fesetround
     libc.src.fenv.fetestexcept
-    libc.src.fenv.fegestexceptflag
+    libc.src.fenv.fetestexceptflag
     libc.src.fenv.feupdateenv
 
     # math.h entrypoints

>From f169cbac4c2f61f7424db5765fa06c173ff72402 Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Tue, 8 Sep 2026 14:39:06 -0700
Subject: [PATCH 05/15] Create type headers for scandir filter and compare
 function pointers and update the unit test.

---
 libc/include/CMakeLists.txt                   |  2 ++
 libc/include/dirent.yaml                      |  6 ++++--
 libc/include/llvm-libc-types/CMakeLists.txt   |  2 ++
 .../llvm-libc-types/__scandir_compare_t.h     | 21 +++++++++++++++++++
 .../llvm-libc-types/__scandir_filter_t.h      | 21 +++++++++++++++++++
 libc/test/src/dirent/scandir_test.cpp         |  4 ++--
 6 files changed, 52 insertions(+), 4 deletions(-)
 create mode 100644 libc/include/llvm-libc-types/__scandir_compare_t.h
 create mode 100644 libc/include/llvm-libc-types/__scandir_filter_t.h

diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index b6a059e67c355..06ee9b1b1f230 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -47,6 +47,8 @@ add_header_macro(
   dirent.h
   DEPENDS
     .llvm_libc_common_h
+    .llvm-libc-types.__scandir_filter_t
+    .llvm-libc-types.__scandir_compare_t
     .llvm-libc-types.ino_t
     .llvm-libc-types.DIR
     .llvm-libc-types.struct_dirent
diff --git a/libc/include/dirent.yaml b/libc/include/dirent.yaml
index 8dc939fa1158a..95957303270ab 100644
--- a/libc/include/dirent.yaml
+++ b/libc/include/dirent.yaml
@@ -26,6 +26,8 @@ types:
   - type_name: reclen_t
   - type_name: size_t
   - type_name: ssize_t
+  - type_name: __scandir_filter_t
+  - type_name: __scandir_compare_t
 functions:
   - name: alphasort
     standards:
@@ -71,5 +73,5 @@ functions:
     arguments:
       - type: const char *
       - type: struct dirent ***
-      - type: int (*)(const struct dirent *)
-      - type: int (*)(const struct dirent **, const struct dirent **)
+      - type: __scandir_filter_t
+      - type: __scandir_compare_t
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index cc7b4df1202ff..6464fdffa696b 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -761,3 +761,5 @@ add_header(__action_fn_t
     .VISIT
 )
 add_header(__free_fn_t HDR __free_fn_t.h)
+add_header(__scandir_filter_t HDR __scandir_filter_t.h DEPENDS .struct_dirent)
+add_header(__scandir_compare_t HDR __scandir_compare_t.h DEPENDS .struct_dirent)
diff --git a/libc/include/llvm-libc-types/__scandir_compare_t.h b/libc/include/llvm-libc-types/__scandir_compare_t.h
new file mode 100644
index 0000000000000..0039188ee6470
--- /dev/null
+++ b/libc/include/llvm-libc-types/__scandir_compare_t.h
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Definition of __scandir_compare_t type.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TYPES___SCANDIR_COMPARE_T_H
+#define LLVM_LIBC_TYPES___SCANDIR_COMPARE_T_H
+
+#include "struct_dirent.h"
+
+typedef int (*__scandir_compare_t)(const struct dirent **, const struct dirent **);
+
+#endif  // LLVM_LIBC_TYPES___SCANDIR_COMPARE_T_H
diff --git a/libc/include/llvm-libc-types/__scandir_filter_t.h b/libc/include/llvm-libc-types/__scandir_filter_t.h
new file mode 100644
index 0000000000000..204c950ee1e2c
--- /dev/null
+++ b/libc/include/llvm-libc-types/__scandir_filter_t.h
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Definition of __scandir_filter type.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TYPES___SCANDIR_FILTER_T_H
+#define LLVM_LIBC_TYPES___SCANDIR_FILTER_T_H
+
+#include "struct_dirent.h"
+
+typedef int (*__scandir_filter_t)(const struct dirent *);
+
+#endif  // LLVM_LIBC_TYPES___SCANDIR_FILTER_H
diff --git a/libc/test/src/dirent/scandir_test.cpp b/libc/test/src/dirent/scandir_test.cpp
index 5be2c9c636bdb..0284da9270eb7 100644
--- a/libc/test/src/dirent/scandir_test.cpp
+++ b/libc/test/src/dirent/scandir_test.cpp
@@ -17,10 +17,10 @@
 #include "test/UnitTest/ErrnoSetterMatcher.h"
 #include "test/UnitTest/Test.h"
 
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
 using LlvmLibcScandirTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
-using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
 
 TEST_F(LlvmLibcScandirTest, TestBadDirname) {
   struct dirent **namelist;
-  ASSERT_THAT(LIBC_NAMESPACE::scandir("", &namelist, NULL, NULL), Fails(ENOTDIR, -1));
+  ASSERT_THAT(LIBC_NAMESPACE::scandir("", &namelist, NULL, NULL), Fails(ENOENT, -1));
 }

>From 6da7c73526179c47e64d3984205dd938803832a5 Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Wed, 9 Sep 2026 17:04:19 -0700
Subject: [PATCH 06/15] Implement readdir loop.

---
 libc/src/dirent/CMakeLists.txt        |  3 +++
 libc/src/dirent/scandir.cpp           | 25 ++++++++++++++++++++++---
 libc/test/src/dirent/scandir_test.cpp |  5 +++++
 3 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/libc/src/dirent/CMakeLists.txt b/libc/src/dirent/CMakeLists.txt
index 4ad2a8b286a03..6fe1d23ac702b 100644
--- a/libc/src/dirent/CMakeLists.txt
+++ b/libc/src/dirent/CMakeLists.txt
@@ -59,7 +59,10 @@ add_entrypoint_object(
     libc.hdr.types.struct_dirent
     libc.src.__support.common
     libc.src.__support.macros.config
+    libc.src.dirent.closedir
     libc.src.dirent.opendir
+    libc.src.dirent.readdir
+    libc.src.errno.errno
 )
 
 add_entrypoint_object(
diff --git a/libc/src/dirent/scandir.cpp b/libc/src/dirent/scandir.cpp
index 45351d7aad034..b8f720930801d 100644
--- a/libc/src/dirent/scandir.cpp
+++ b/libc/src/dirent/scandir.cpp
@@ -16,7 +16,10 @@
 #include "hdr/types/struct_dirent.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
+#include "src/__support/libc_errno.h"
+#include "src/dirent/closedir.h"
 #include "src/dirent/opendir.h"
+#include "src/dirent/readdir.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
@@ -26,15 +29,31 @@ LLVM_LIBC_FUNCTION(int, scandir,
                     int (*compar)(const struct dirent **,
                                   const struct dirent **))) {
   (void)namelist;
-  (void)sel;
   (void)compar;
 
-  DIR *dir_fd = opendir(dir);
+  DIR *dir_fd = LIBC_NAMESPACE::opendir(dir);
   if (dir_fd == nullptr) {
-    // opendir set errno
+    // errno set by opendir
     return -1;
   }
 
+  struct dirent *entry = nullptr;
+  do {
+    libc_errno = 0;
+    entry = LIBC_NAMESPACE::readdir(dir_fd);
+    if (libc_errno != 0) {
+      // errno set by readdir
+      return -1;
+    }
+
+    if (sel != nullptr && !sel(entry)) {
+      continue;
+    }
+
+  } while (entry != nullptr);
+
+
+  LIBC_NAMESPACE::closedir(dir_fd);
 
   return 0;
 }
diff --git a/libc/test/src/dirent/scandir_test.cpp b/libc/test/src/dirent/scandir_test.cpp
index 0284da9270eb7..a7da06c9853de 100644
--- a/libc/test/src/dirent/scandir_test.cpp
+++ b/libc/test/src/dirent/scandir_test.cpp
@@ -20,6 +20,11 @@
 using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
 using LlvmLibcScandirTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
 
+TEST_F(LlvmLibcScandirTest, TestDummy) {
+  struct dirent **namelist;
+  ASSERT_THAT(LIBC_NAMESPACE::scandir("/tmp", &namelist, NULL, NULL), Succeeds());
+}
+
 TEST_F(LlvmLibcScandirTest, TestBadDirname) {
   struct dirent **namelist;
   ASSERT_THAT(LIBC_NAMESPACE::scandir("", &namelist, NULL, NULL), Fails(ENOENT, -1));

>From d1c97077001f4d48c07bb6d26ca138cd37710dd2 Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Fri, 11 Sep 2026 12:29:18 -0700
Subject: [PATCH 07/15] Minor changes and fixes, commit before sync.

---
 libc/include/CMakeLists.txt    |  2 +-
 libc/include/dirent.yaml       |  2 +-
 libc/src/dirent/CMakeLists.txt |  1 +
 libc/src/dirent/scandir.cpp    | 19 ++++++++++++++-----
 4 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 06ee9b1b1f230..9e4039cd1a68a 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -47,8 +47,8 @@ add_header_macro(
   dirent.h
   DEPENDS
     .llvm_libc_common_h
-    .llvm-libc-types.__scandir_filter_t
     .llvm-libc-types.__scandir_compare_t
+    .llvm-libc-types.__scandir_filter_t
     .llvm-libc-types.ino_t
     .llvm-libc-types.DIR
     .llvm-libc-types.struct_dirent
diff --git a/libc/include/dirent.yaml b/libc/include/dirent.yaml
index 95957303270ab..ca8d6c6619127 100644
--- a/libc/include/dirent.yaml
+++ b/libc/include/dirent.yaml
@@ -26,8 +26,8 @@ types:
   - type_name: reclen_t
   - type_name: size_t
   - type_name: ssize_t
-  - type_name: __scandir_filter_t
   - type_name: __scandir_compare_t
+  - type_name: __scandir_filter_t
 functions:
   - name: alphasort
     standards:
diff --git a/libc/src/dirent/CMakeLists.txt b/libc/src/dirent/CMakeLists.txt
index 6fe1d23ac702b..e81d16e83bb6f 100644
--- a/libc/src/dirent/CMakeLists.txt
+++ b/libc/src/dirent/CMakeLists.txt
@@ -63,6 +63,7 @@ add_entrypoint_object(
     libc.src.dirent.opendir
     libc.src.dirent.readdir
     libc.src.errno.errno
+    libc.src.stdlib.qsort_util
 )
 
 add_entrypoint_object(
diff --git a/libc/src/dirent/scandir.cpp b/libc/src/dirent/scandir.cpp
index b8f720930801d..9c1d5c2ff8245 100644
--- a/libc/src/dirent/scandir.cpp
+++ b/libc/src/dirent/scandir.cpp
@@ -14,22 +14,24 @@
 #include "src/dirent/scandir.h"
 
 #include "hdr/types/struct_dirent.h"
+/*
+#include "include/llvm-libc-types/__scandir_compare_t.h"
+#include "include/llvm-libc-types/__scandir_filter_t.h"
+*/
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/libc_errno.h"
 #include "src/dirent/closedir.h"
 #include "src/dirent/opendir.h"
 #include "src/dirent/readdir.h"
+#include "src/stdlib/qsort_util.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, scandir,
                    (const char *dir, struct dirent ***namelist,
-                    int (*sel)(const struct dirent *),
-                    int (*compar)(const struct dirent **,
-                                  const struct dirent **))) {
+                    __scandir_filter_t filter, __scandir_compare_t compare)) {
   (void)namelist;
-  (void)compar;
 
   DIR *dir_fd = LIBC_NAMESPACE::opendir(dir);
   if (dir_fd == nullptr) {
@@ -37,7 +39,12 @@ LLVM_LIBC_FUNCTION(int, scandir,
     return -1;
   }
 
+  struct dirent **namelist_local = {};
+  (void)namelist_local;
+
   struct dirent *entry = nullptr;
+  size_t count = 0;
+
   do {
     libc_errno = 0;
     entry = LIBC_NAMESPACE::readdir(dir_fd);
@@ -46,12 +53,14 @@ LLVM_LIBC_FUNCTION(int, scandir,
       return -1;
     }
 
-    if (sel != nullptr && !sel(entry)) {
+    if (filter != nullptr && !filter(entry)) {
       continue;
     }
 
   } while (entry != nullptr);
 
+  if (compare != nullptr) {
+  }
 
   LIBC_NAMESPACE::closedir(dir_fd);
 

>From 95bd74d9bd9b59449432f248ce0d751bae5af828 Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Fri, 11 Sep 2026 16:22:18 -0700
Subject: [PATCH 08/15] Implement complete scandir functionality.

---
 libc/src/dirent/CMakeLists.txt |  3 ++
 libc/src/dirent/scandir.cpp    | 74 +++++++++++++++++++++++++++-------
 2 files changed, 62 insertions(+), 15 deletions(-)

diff --git a/libc/src/dirent/CMakeLists.txt b/libc/src/dirent/CMakeLists.txt
index e81d16e83bb6f..befea755909d6 100644
--- a/libc/src/dirent/CMakeLists.txt
+++ b/libc/src/dirent/CMakeLists.txt
@@ -56,8 +56,11 @@ add_entrypoint_object(
   HDRS
     scandir.h
   DEPENDS
+    libc.hdr.types.size_t
     libc.hdr.types.struct_dirent
+    libc.src.__support.alloc_checker
     libc.src.__support.common
+    libc.src.__support.CPP.vector
     libc.src.__support.macros.config
     libc.src.dirent.closedir
     libc.src.dirent.opendir
diff --git a/libc/src/dirent/scandir.cpp b/libc/src/dirent/scandir.cpp
index 9c1d5c2ff8245..9c693bfc23ba3 100644
--- a/libc/src/dirent/scandir.cpp
+++ b/libc/src/dirent/scandir.cpp
@@ -7,61 +7,105 @@
 //===----------------------------------------------------------------------===//
 ///
 /// \file
-/// Implementation of getpriority.
+/// Implementation of scandir.
 ///
 //===----------------------------------------------------------------------===//
 
 #include "src/dirent/scandir.h"
 
+#include "hdr/types/size_t.h"
 #include "hdr/types/struct_dirent.h"
-/*
-#include "include/llvm-libc-types/__scandir_compare_t.h"
-#include "include/llvm-libc-types/__scandir_filter_t.h"
-*/
+#include "src/__support/alloc-checker.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/libc_errno.h"
+#include "src/__support/CPP/vector.h"
 #include "src/dirent/closedir.h"
 #include "src/dirent/opendir.h"
 #include "src/dirent/readdir.h"
 #include "src/stdlib/qsort_util.h"
+#include "src/string/memcpy.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
+void free_entries(LIBC_NAMESPACE::cpp::vector<struct dirent *> &entries) {
+  for (struct dirent *entry: entries) {
+    delete entry;
+  }
+}
+
 LLVM_LIBC_FUNCTION(int, scandir,
                    (const char *dir, struct dirent ***namelist,
                     __scandir_filter_t filter, __scandir_compare_t compare)) {
-  (void)namelist;
-
   DIR *dir_fd = LIBC_NAMESPACE::opendir(dir);
   if (dir_fd == nullptr) {
     // errno set by opendir
     return -1;
   }
 
-  struct dirent **namelist_local = {};
-  (void)namelist_local;
-
-  struct dirent *entry = nullptr;
-  size_t count = 0;
+  LIBC_NAMESPACE::cpp::vector<struct dirent*> entries;
 
-  do {
+  while (true) {
     libc_errno = 0;
-    entry = LIBC_NAMESPACE::readdir(dir_fd);
+    struct dirent *entry = LIBC_NAMESPACE::readdir(dir_fd);
     if (libc_errno != 0) {
+      free_entries(entries);
+      LIBC_NAMESPACE::closedir(dir_fd);
       // errno set by readdir
       return -1;
     }
 
+    if (entry == nullptr) {
+      break;
+    }
+
     if (filter != nullptr && !filter(entry)) {
       continue;
     }
 
-  } while (entry != nullptr);
+    AllocChecker ac;
+    struct dirent *new_entry = new (ac) struct dirent;
+    if (!ac || new_entry == NULL) {
+      free_entries(entries);
+      LIBC_NAMESPACE::closedir(dir_fd);
+      libc_errno = ENOMEM;
+      return -1;
+    }
+
+    LIBC_NAMESPACE::memcpy(new_entry, entry, sizeof(struct dirent));
+
+    if (!entries.push_back(new_entry)) {
+      free_entries(entries);
+      LIBC_NAMESPACE::closedir(dir_fd);
+      libc_errno = ENOMEM;
+      return -1;
+    }
+  }
 
   if (compare != nullptr) {
+    auto cmp_fn = [compare](const void *a, const void *b) {
+      auto left = static_cast<const struct dirent **>(const_cast<void *>(a));
+      auto right = static_cast<const struct dirent **>(const_cast<void *>(b));
+      return compare(left, right);
+    };
+    internal::unstable_sort(entries.data(), entries.size(), sizeof(struct dirent*), cmp_fn);
+  }
+
+  AllocChecker ac;
+  struct dirent **result = new (ac) struct dirent*[entries.size()];
+  if (!ac) {
+    free_entries(entries);
+    LIBC_NAMESPACE::closedir(dir_fd);
+    libc_errno = ENOMEM;
+    return -1;
   }
 
+  for (size_t i = 0; i < entries.size(); ++i) {
+    result[i] = entries[i];
+  }
+
+  *namelist = result;
+
   LIBC_NAMESPACE::closedir(dir_fd);
 
   return 0;

>From 7051645f687c18f12de27110af365a5b51a18423 Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Fri, 11 Sep 2026 17:14:45 -0700
Subject: [PATCH 09/15] Fix return value and refactor scandir.

---
 libc/src/dirent/scandir.cpp | 52 ++++++++++++++-----------------------
 1 file changed, 20 insertions(+), 32 deletions(-)

diff --git a/libc/src/dirent/scandir.cpp b/libc/src/dirent/scandir.cpp
index 9c693bfc23ba3..ab64961ab7276 100644
--- a/libc/src/dirent/scandir.cpp
+++ b/libc/src/dirent/scandir.cpp
@@ -28,12 +28,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-void free_entries(LIBC_NAMESPACE::cpp::vector<struct dirent *> &entries) {
-  for (struct dirent *entry: entries) {
-    delete entry;
-  }
-}
-
 LLVM_LIBC_FUNCTION(int, scandir,
                    (const char *dir, struct dirent ***namelist,
                     __scandir_filter_t filter, __scandir_compare_t compare)) {
@@ -48,14 +42,8 @@ LLVM_LIBC_FUNCTION(int, scandir,
   while (true) {
     libc_errno = 0;
     struct dirent *entry = LIBC_NAMESPACE::readdir(dir_fd);
-    if (libc_errno != 0) {
-      free_entries(entries);
-      LIBC_NAMESPACE::closedir(dir_fd);
-      // errno set by readdir
-      return -1;
-    }
-
     if (entry == nullptr) {
+      // If the readdir call failed it set errno
       break;
     }
 
@@ -66,20 +54,32 @@ LLVM_LIBC_FUNCTION(int, scandir,
     AllocChecker ac;
     struct dirent *new_entry = new (ac) struct dirent;
     if (!ac || new_entry == NULL) {
-      free_entries(entries);
-      LIBC_NAMESPACE::closedir(dir_fd);
       libc_errno = ENOMEM;
-      return -1;
+      break;
     }
 
     LIBC_NAMESPACE::memcpy(new_entry, entry, sizeof(struct dirent));
 
     if (!entries.push_back(new_entry)) {
-      free_entries(entries);
-      LIBC_NAMESPACE::closedir(dir_fd);
       libc_errno = ENOMEM;
-      return -1;
+      break;
+    }
+  }
+
+  LIBC_NAMESPACE::closedir(dir_fd);
+
+
+  AllocChecker ac;
+  struct dirent **result = new (ac) struct dirent*[entries.size()];
+  if (!ac) {
+    libc_errno = ENOMEM;
+  }
+
+  if (libc_errno != 0) {
+    for (struct dirent *entry: entries) {
+      delete entry;
     }
+    return -1;
   }
 
   if (compare != nullptr) {
@@ -91,24 +91,12 @@ LLVM_LIBC_FUNCTION(int, scandir,
     internal::unstable_sort(entries.data(), entries.size(), sizeof(struct dirent*), cmp_fn);
   }
 
-  AllocChecker ac;
-  struct dirent **result = new (ac) struct dirent*[entries.size()];
-  if (!ac) {
-    free_entries(entries);
-    LIBC_NAMESPACE::closedir(dir_fd);
-    libc_errno = ENOMEM;
-    return -1;
-  }
-
   for (size_t i = 0; i < entries.size(); ++i) {
     result[i] = entries[i];
   }
 
   *namelist = result;
-
-  LIBC_NAMESPACE::closedir(dir_fd);
-
-  return 0;
+  return static_cast<int>(entries.size());
 }
 
 } // namespace LIBC_NAMESPACE_DECL

>From af2b920749efbff7d0bcfb7df2aa1d66a13958bc Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Fri, 11 Sep 2026 17:45:22 -0700
Subject: [PATCH 10/15] Implement scandir unit test skeleton and helper
 functions.

---
 libc/test/src/dirent/CMakeLists.txt   |  7 ++++
 libc/test/src/dirent/scandir_test.cpp | 54 ++++++++++++++++++++++++++-
 2 files changed, 59 insertions(+), 2 deletions(-)

diff --git a/libc/test/src/dirent/CMakeLists.txt b/libc/test/src/dirent/CMakeLists.txt
index 309045f8a9536..1dc41ebd681d9 100644
--- a/libc/test/src/dirent/CMakeLists.txt
+++ b/libc/test/src/dirent/CMakeLists.txt
@@ -46,7 +46,14 @@ add_libc_test(
     scandir_test.cpp
   DEPENDS
     libc.hdr.types.struct_dirent
+    libc.src.__support.OSUtil.path
     libc.src.dirent.scandir
+    libc.src.stdio.asprintf
+    libc.src.stdio.fopen
+    libc.src.stdio.fclose
+    libc.src.stdlib.mkdtemp
+    libc.src.string.strdup
+    libc.src.unistd.rmdir
     libc.test.UnitTest.ErrnoCheckingTest
     libc.test.UnitTest.ErrnoSetterMatcher
 )
diff --git a/libc/test/src/dirent/scandir_test.cpp b/libc/test/src/dirent/scandir_test.cpp
index a7da06c9853de..b06903cab1ab7 100644
--- a/libc/test/src/dirent/scandir_test.cpp
+++ b/libc/test/src/dirent/scandir_test.cpp
@@ -12,7 +12,14 @@
 //===----------------------------------------------------------------------===//
 
 #include "hdr/types/struct_dirent.h"
+#include "src/__support/OSUtil/path.h"
 #include "src/dirent/scandir.h"
+#include "src/stdio/asprintf.h"
+#include "src/stdio/fopen.h"
+#include "src/stdio/fclose.h"
+#include "src/stdlib/mkdtemp.h"
+#include "src/string/strdup.h"
+#include "src/unistd/rmdir.h"
 #include "test/UnitTest/ErrnoCheckingTest.h"
 #include "test/UnitTest/ErrnoSetterMatcher.h"
 #include "test/UnitTest/Test.h"
@@ -20,11 +27,54 @@
 using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
 using LlvmLibcScandirTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
 
-TEST_F(LlvmLibcScandirTest, TestDummy) {
+constexpr char TEMPLATE[] = "tmp_XXXXXX";
+
+// A dir alwasys has '.' and '..' in it.
+constexpr int MINIMUM_ENTRIES = 2;
+
+bool create_file(char *dir, const char *name) {
+  char *path = nullptr;
+
+  if (LIBC_NAMESPACE::asprintf(&path, "%s%c%s", dir, LIBC_NAMESPACE::path::SEPARATOR, name) == -1) {
+    return false;
+  }
+
+  FILE *file = LIBC_NAMESPACE::fopen(path, "w");
+  if (file == nullptr) {
+    return false;
+  }
+
+  if (LIBC_NAMESPACE::fclose(file) == -1) {
+    return false;
+  }
+
+  return true;
+}
+
+
+TEST_F(LlvmLibcScandirTest, TestBasic) {
+
+  char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path(TEMPLATE));
+  ASSERT_NE(tmpl, nullptr);
+  ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl));
+
+  const char *filename_a = libc_make_test_file_path("a");
+  const char *filename_b = libc_make_test_file_path("file_b");
+
+  ASSERT_TRUE(create_file(tmpl, filename_a));
+  ASSERT_TRUE(create_file(tmpl, filename_b));
+
   struct dirent **namelist;
-  ASSERT_THAT(LIBC_NAMESPACE::scandir("/tmp", &namelist, NULL, NULL), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::scandir(tmpl, &namelist, NULL, NULL), Succeeds(MINIMUM_ENTRIES + 2));
+
+  // TODO: Implement file deletion!
+  /*
+  ASSERT_THAT(LIBC_NAMESPACE::rmdir(tmpl), Succeeds());
+  free(tmpl);
+  */
 }
 
+
 TEST_F(LlvmLibcScandirTest, TestBadDirname) {
   struct dirent **namelist;
   ASSERT_THAT(LIBC_NAMESPACE::scandir("", &namelist, NULL, NULL), Fails(ENOENT, -1));

>From 5e5bfe291a5fcdccd9f09397b90370e7cff1e772 Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Sat, 12 Sep 2026 16:23:43 -0700
Subject: [PATCH 11/15] Move scandir implementation to linux/ and clean up
 empty dir test.

---
 libc/src/dirent/CMakeLists.txt          | 32 ++++++++++---------------
 libc/src/dirent/linux/CMakeLists.txt    | 19 +++++++++++++++
 libc/src/dirent/{ => linux}/scandir.cpp |  4 +++-
 libc/test/src/dirent/CMakeLists.txt     |  1 +
 libc/test/src/dirent/scandir_test.cpp   | 31 ++++++++++++++----------
 5 files changed, 54 insertions(+), 33 deletions(-)
 create mode 100644 libc/src/dirent/linux/CMakeLists.txt
 rename libc/src/dirent/{ => linux}/scandir.cpp (93%)

diff --git a/libc/src/dirent/CMakeLists.txt b/libc/src/dirent/CMakeLists.txt
index befea755909d6..b5237333e2931 100644
--- a/libc/src/dirent/CMakeLists.txt
+++ b/libc/src/dirent/CMakeLists.txt
@@ -1,3 +1,8 @@
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+  add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+endif()
+
+
 add_entrypoint_object(
   opendir
   SRCS
@@ -49,26 +54,6 @@ add_entrypoint_object(
     libc.src.errno.errno
 )
 
-add_entrypoint_object(
-  scandir
-  SRCS
-    scandir.cpp
-  HDRS
-    scandir.h
-  DEPENDS
-    libc.hdr.types.size_t
-    libc.hdr.types.struct_dirent
-    libc.src.__support.alloc_checker
-    libc.src.__support.common
-    libc.src.__support.CPP.vector
-    libc.src.__support.macros.config
-    libc.src.dirent.closedir
-    libc.src.dirent.opendir
-    libc.src.dirent.readdir
-    libc.src.errno.errno
-    libc.src.stdlib.qsort_util
-)
-
 add_entrypoint_object(
   fdopendir
   SRCS
@@ -82,3 +67,10 @@ add_entrypoint_object(
     libc.src.__support.File.dir
     libc.src.errno.errno
 )
+
+add_entrypoint_object(
+  scandir
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.scandir
+)
diff --git a/libc/src/dirent/linux/CMakeLists.txt b/libc/src/dirent/linux/CMakeLists.txt
new file mode 100644
index 0000000000000..e68c3d0e240c6
--- /dev/null
+++ b/libc/src/dirent/linux/CMakeLists.txt
@@ -0,0 +1,19 @@
+add_entrypoint_object(
+  scandir
+  SRCS
+    scandir.cpp
+  HDRS
+    ../scandir.h
+  DEPENDS
+    libc.hdr.types.size_t
+    libc.hdr.types.struct_dirent
+    libc.src.__support.alloc_checker
+    libc.src.__support.common
+    libc.src.__support.CPP.vector
+    libc.src.__support.macros.config
+    libc.src.dirent.closedir
+    libc.src.dirent.opendir
+    libc.src.dirent.readdir
+    libc.src.errno.errno
+    libc.src.stdlib.qsort_util
+)
diff --git a/libc/src/dirent/scandir.cpp b/libc/src/dirent/linux/scandir.cpp
similarity index 93%
rename from libc/src/dirent/scandir.cpp
rename to libc/src/dirent/linux/scandir.cpp
index ab64961ab7276..d1f1612042986 100644
--- a/libc/src/dirent/scandir.cpp
+++ b/libc/src/dirent/linux/scandir.cpp
@@ -58,7 +58,9 @@ LLVM_LIBC_FUNCTION(int, scandir,
       break;
     }
 
-    LIBC_NAMESPACE::memcpy(new_entry, entry, sizeof(struct dirent));
+    // struct dirent contains an equivalent of flexible array memeber,
+    // which makes sizeof unreliable, hence we use d_reclen.
+    LIBC_NAMESPACE::memcpy(new_entry, entry, entry->d_reclen);
 
     if (!entries.push_back(new_entry)) {
       libc_errno = ENOMEM;
diff --git a/libc/test/src/dirent/CMakeLists.txt b/libc/test/src/dirent/CMakeLists.txt
index 1dc41ebd681d9..c1e7d3288bfad 100644
--- a/libc/test/src/dirent/CMakeLists.txt
+++ b/libc/test/src/dirent/CMakeLists.txt
@@ -53,6 +53,7 @@ add_libc_test(
     libc.src.stdio.fclose
     libc.src.stdlib.mkdtemp
     libc.src.string.strdup
+    libc.src.string.strncmp
     libc.src.unistd.rmdir
     libc.test.UnitTest.ErrnoCheckingTest
     libc.test.UnitTest.ErrnoSetterMatcher
diff --git a/libc/test/src/dirent/scandir_test.cpp b/libc/test/src/dirent/scandir_test.cpp
index b06903cab1ab7..377412c5cd52a 100644
--- a/libc/test/src/dirent/scandir_test.cpp
+++ b/libc/test/src/dirent/scandir_test.cpp
@@ -19,6 +19,7 @@
 #include "src/stdio/fclose.h"
 #include "src/stdlib/mkdtemp.h"
 #include "src/string/strdup.h"
+#include "src/string/strncmp.h"
 #include "src/unistd/rmdir.h"
 #include "test/UnitTest/ErrnoCheckingTest.h"
 #include "test/UnitTest/ErrnoSetterMatcher.h"
@@ -30,7 +31,7 @@ using LlvmLibcScandirTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
 constexpr char TEMPLATE[] = "tmp_XXXXXX";
 
 // A dir alwasys has '.' and '..' in it.
-constexpr int MINIMUM_ENTRIES = 2;
+constexpr int ENTRIES_MIN = 2;
 
 bool create_file(char *dir, const char *name) {
   char *path = nullptr;
@@ -38,6 +39,7 @@ bool create_file(char *dir, const char *name) {
   if (LIBC_NAMESPACE::asprintf(&path, "%s%c%s", dir, LIBC_NAMESPACE::path::SEPARATOR, name) == -1) {
     return false;
   }
+  free(path);
 
   FILE *file = LIBC_NAMESPACE::fopen(path, "w");
   if (file == nullptr) {
@@ -52,26 +54,31 @@ bool create_file(char *dir, const char *name) {
 }
 
 
-TEST_F(LlvmLibcScandirTest, TestBasic) {
+TEST_F(LlvmLibcScandirTest, TestEmptyDir) {
 
   char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path(TEMPLATE));
   ASSERT_NE(tmpl, nullptr);
   ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl));
 
-  const char *filename_a = libc_make_test_file_path("a");
-  const char *filename_b = libc_make_test_file_path("file_b");
-
-  ASSERT_TRUE(create_file(tmpl, filename_a));
-  ASSERT_TRUE(create_file(tmpl, filename_b));
-
   struct dirent **namelist;
-  ASSERT_THAT(LIBC_NAMESPACE::scandir(tmpl, &namelist, NULL, NULL), Succeeds(MINIMUM_ENTRIES + 2));
+  ASSERT_THAT(LIBC_NAMESPACE::scandir(tmpl, &namelist, NULL, NULL), Succeeds(ENTRIES_MIN));
+  // ASSERT_STREQ(namelist[1]->d_name, ".");
+  // Order of namelist is not guaranteed so we can't easily use ASSERT_STREQ
+  ASSERT_TRUE(
+      (LIBC_NAMESPACE::strncmp(namelist[0]->d_name, ".",  1) == 0 &&
+       LIBC_NAMESPACE::strncmp(namelist[1]->d_name, "..", 2) == 0) ||
+      (LIBC_NAMESPACE::strncmp(namelist[0]->d_name, "..", 2) == 0 &&
+       LIBC_NAMESPACE::strncmp(namelist[1]->d_name, ".",  1) == 0));
+
+  // We also test that both ordering can't be true at the same time.
+  ASSERT_FALSE(
+      (LIBC_NAMESPACE::strncmp(namelist[0]->d_name, ".",  1) == 0 &&
+       LIBC_NAMESPACE::strncmp(namelist[1]->d_name, "..", 2) == 0) &&
+      (LIBC_NAMESPACE::strncmp(namelist[0]->d_name, "..", 2) == 0 &&
+       LIBC_NAMESPACE::strncmp(namelist[1]->d_name, ".",  1) == 0));
 
-  // TODO: Implement file deletion!
-  /*
   ASSERT_THAT(LIBC_NAMESPACE::rmdir(tmpl), Succeeds());
   free(tmpl);
-  */
 }
 
 

>From ae11ac69a04ce46db5523de0f9b1b2881d220a5c Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Sat, 12 Sep 2026 17:10:41 -0700
Subject: [PATCH 12/15] Use malloc instead of AllocChecker in scandir

---
 libc/src/dirent/linux/CMakeLists.txt |  3 ++-
 libc/src/dirent/linux/scandir.cpp    | 38 ++++++++++++++++------------
 2 files changed, 24 insertions(+), 17 deletions(-)

diff --git a/libc/src/dirent/linux/CMakeLists.txt b/libc/src/dirent/linux/CMakeLists.txt
index e68c3d0e240c6..71ec9be576623 100644
--- a/libc/src/dirent/linux/CMakeLists.txt
+++ b/libc/src/dirent/linux/CMakeLists.txt
@@ -5,9 +5,10 @@ add_entrypoint_object(
   HDRS
     ../scandir.h
   DEPENDS
+    libc.hdr.func.free
+    libc.hdr.func.malloc
     libc.hdr.types.size_t
     libc.hdr.types.struct_dirent
-    libc.src.__support.alloc_checker
     libc.src.__support.common
     libc.src.__support.CPP.vector
     libc.src.__support.macros.config
diff --git a/libc/src/dirent/linux/scandir.cpp b/libc/src/dirent/linux/scandir.cpp
index d1f1612042986..37fd06075b136 100644
--- a/libc/src/dirent/linux/scandir.cpp
+++ b/libc/src/dirent/linux/scandir.cpp
@@ -13,9 +13,10 @@
 
 #include "src/dirent/scandir.h"
 
+#include "hdr/func/free.h"
+#include "hdr/func/malloc.h"
 #include "hdr/types/size_t.h"
 #include "hdr/types/struct_dirent.h"
-#include "src/__support/alloc-checker.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/libc_errno.h"
@@ -23,6 +24,7 @@
 #include "src/dirent/closedir.h"
 #include "src/dirent/opendir.h"
 #include "src/dirent/readdir.h"
+#include "src/stdlib/malloc.h"
 #include "src/stdlib/qsort_util.h"
 #include "src/string/memcpy.h"
 
@@ -37,6 +39,7 @@ LLVM_LIBC_FUNCTION(int, scandir,
     return -1;
   }
 
+  int saved_errno = 0;
   LIBC_NAMESPACE::cpp::vector<struct dirent*> entries;
 
   while (true) {
@@ -44,43 +47,46 @@ LLVM_LIBC_FUNCTION(int, scandir,
     struct dirent *entry = LIBC_NAMESPACE::readdir(dir_fd);
     if (entry == nullptr) {
       // If the readdir call failed it set errno
+      saved_errno = libc_errno;
       break;
     }
 
+    // Note, filter may modify errno
     if (filter != nullptr && !filter(entry)) {
       continue;
     }
 
-    AllocChecker ac;
-    struct dirent *new_entry = new (ac) struct dirent;
-    if (!ac || new_entry == NULL) {
-      libc_errno = ENOMEM;
-      break;
+    // struct dirent contains an equivalent of flexible array memeber we
+    // allocate with malloc and use d_reclen as size.
+    struct dirent *new_entry = static_cast<struct dirent*>(::malloc(entry->d_reclen));
+    if (new_entry == nullptr) {
+      saved_errno = ENOMEM;
     }
-
-    // struct dirent contains an equivalent of flexible array memeber,
-    // which makes sizeof unreliable, hence we use d_reclen.
     LIBC_NAMESPACE::memcpy(new_entry, entry, entry->d_reclen);
 
     if (!entries.push_back(new_entry)) {
-      libc_errno = ENOMEM;
+      free(new_entry);
+      saved_errno = ENOMEM;
       break;
     }
   }
 
+  // Closedir may modify errno and set it to EBADF, which is not amongst
+  // POSIX-defined error codes for scandir. So we ignore closedir's errno.
   LIBC_NAMESPACE::closedir(dir_fd);
 
+  struct dirent **result = static_cast<struct dirent**>(
+      ::malloc(entries.size() * sizeof(struct dirent *)));
 
-  AllocChecker ac;
-  struct dirent **result = new (ac) struct dirent*[entries.size()];
-  if (!ac) {
-    libc_errno = ENOMEM;
+  if (result == nullptr) {
+    saved_errno = ENOMEM;
   }
 
-  if (libc_errno != 0) {
+  if (saved_errno != 0) {
     for (struct dirent *entry: entries) {
-      delete entry;
+      ::free(entry);
     }
+    libc_errno = saved_errno;
     return -1;
   }
 

>From ed3ef6633161784f2836ca22eb96cd4f874feabe Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Sat, 12 Sep 2026 17:54:21 -0700
Subject: [PATCH 13/15] Implement basic filter and compare unit tests for
 scandir

---
 libc/test/src/dirent/CMakeLists.txt   |  2 +
 libc/test/src/dirent/scandir_test.cpp | 77 ++++++++++++++++++++++-----
 2 files changed, 67 insertions(+), 12 deletions(-)

diff --git a/libc/test/src/dirent/CMakeLists.txt b/libc/test/src/dirent/CMakeLists.txt
index c1e7d3288bfad..81bc4dd19bd5b 100644
--- a/libc/test/src/dirent/CMakeLists.txt
+++ b/libc/test/src/dirent/CMakeLists.txt
@@ -51,7 +51,9 @@ add_libc_test(
     libc.src.stdio.asprintf
     libc.src.stdio.fopen
     libc.src.stdio.fclose
+    libc.src.stdio.remove
     libc.src.stdlib.mkdtemp
+    libc.src.string.strcoll
     libc.src.string.strdup
     libc.src.string.strncmp
     libc.src.unistd.rmdir
diff --git a/libc/test/src/dirent/scandir_test.cpp b/libc/test/src/dirent/scandir_test.cpp
index 377412c5cd52a..2acc0e3ed47c4 100644
--- a/libc/test/src/dirent/scandir_test.cpp
+++ b/libc/test/src/dirent/scandir_test.cpp
@@ -17,7 +17,9 @@
 #include "src/stdio/asprintf.h"
 #include "src/stdio/fopen.h"
 #include "src/stdio/fclose.h"
+#include "src/stdio/remove.h"
 #include "src/stdlib/mkdtemp.h"
+#include "src/string/strcoll.h"
 #include "src/string/strdup.h"
 #include "src/string/strncmp.h"
 #include "src/unistd/rmdir.h"
@@ -33,15 +35,14 @@ constexpr char TEMPLATE[] = "tmp_XXXXXX";
 // A dir alwasys has '.' and '..' in it.
 constexpr int ENTRIES_MIN = 2;
 
-bool create_file(char *dir, const char *name) {
+char *join_path(char *dir, const char *filename) {
   char *path = nullptr;
-
-  if (LIBC_NAMESPACE::asprintf(&path, "%s%c%s", dir, LIBC_NAMESPACE::path::SEPARATOR, name) == -1) {
-    return false;
+  if (LIBC_NAMESPACE::asprintf(&path, "%s%c%s", dir, LIBC_NAMESPACE::path::SEPARATOR, filename) == -1) {
+    return nullptr;
   }
-  free(path);
-
-  FILE *file = LIBC_NAMESPACE::fopen(path, "w");
+  return path;
+}
+bool create_empty_file(char *path) { FILE *file = LIBC_NAMESPACE::fopen(path, "w");
   if (file == nullptr) {
     return false;
   }
@@ -49,20 +50,25 @@ bool create_file(char *dir, const char *name) {
   if (LIBC_NAMESPACE::fclose(file) == -1) {
     return false;
   }
-
   return true;
 }
 
 
-TEST_F(LlvmLibcScandirTest, TestEmptyDir) {
+int alphasort(const struct dirent **a, const struct dirent **b) {
+  return LIBC_NAMESPACE::strcoll((*a)->d_name, (*b)->d_name);
+}
+
+int skip_hidden(const struct dirent *entry) {
+    return entry->d_name[0] != '.';
+}
 
+TEST_F(LlvmLibcScandirTest, TestEmptyDir) {
   char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path(TEMPLATE));
   ASSERT_NE(tmpl, nullptr);
   ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl));
 
   struct dirent **namelist;
-  ASSERT_THAT(LIBC_NAMESPACE::scandir(tmpl, &namelist, NULL, NULL), Succeeds(ENTRIES_MIN));
-  // ASSERT_STREQ(namelist[1]->d_name, ".");
+  ASSERT_THAT(LIBC_NAMESPACE::scandir(tmpl, &namelist, nullptr, nullptr), Succeeds(ENTRIES_MIN));
   // Order of namelist is not guaranteed so we can't easily use ASSERT_STREQ
   ASSERT_TRUE(
       (LIBC_NAMESPACE::strncmp(namelist[0]->d_name, ".",  1) == 0 &&
@@ -70,7 +76,7 @@ TEST_F(LlvmLibcScandirTest, TestEmptyDir) {
       (LIBC_NAMESPACE::strncmp(namelist[0]->d_name, "..", 2) == 0 &&
        LIBC_NAMESPACE::strncmp(namelist[1]->d_name, ".",  1) == 0));
 
-  // We also test that both ordering can't be true at the same time.
+  // We also test that both orderings can't be true at the same time.
   ASSERT_FALSE(
       (LIBC_NAMESPACE::strncmp(namelist[0]->d_name, ".",  1) == 0 &&
        LIBC_NAMESPACE::strncmp(namelist[1]->d_name, "..", 2) == 0) &&
@@ -81,6 +87,53 @@ TEST_F(LlvmLibcScandirTest, TestEmptyDir) {
   free(tmpl);
 }
 
+TEST_F(LlvmLibcScandirTest, TestDirFilter) {
+  char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path(TEMPLATE));
+  ASSERT_NE(tmpl, nullptr);
+  ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl));
+
+  struct dirent **namelist;
+  ASSERT_THAT(LIBC_NAMESPACE::scandir(tmpl, &namelist, skip_hidden, nullptr), Succeeds(0));
+
+  ASSERT_THAT(LIBC_NAMESPACE::rmdir(tmpl), Succeeds());
+  free(tmpl);
+}
+
+TEST_F(LlvmLibcScandirTest, TestDirSorted) {
+  char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path(TEMPLATE));
+  ASSERT_NE(tmpl, nullptr);
+  ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl));
+
+  char *path_d = join_path(tmpl, "d");
+  ASSERT_TRUE(path_d != nullptr);
+  ASSERT_TRUE(create_empty_file(path_d));
+
+  char *path_a = join_path(tmpl, "a");
+  ASSERT_TRUE(path_a != nullptr);
+  ASSERT_TRUE(create_empty_file(path_a));
+
+  char *path_1 = join_path(tmpl, "1");
+  ASSERT_TRUE(path_1 != nullptr);
+  ASSERT_TRUE(create_empty_file(path_1));
+
+  struct dirent **namelist;
+  ASSERT_THAT(LIBC_NAMESPACE::scandir(tmpl, &namelist, skip_hidden, alphasort), Succeeds(3));
+
+  ASSERT_STREQ(namelist[0]->d_name, "1");
+  ASSERT_STREQ(namelist[1]->d_name, "a");
+  ASSERT_STREQ(namelist[2]->d_name, "d");
+
+  ASSERT_THAT(LIBC_NAMESPACE::remove(path_d), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::remove(path_a), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::remove(path_1), Succeeds());
+
+  free(path_d);
+  free(path_a);
+  free(path_1);
+
+  ASSERT_THAT(LIBC_NAMESPACE::rmdir(tmpl), Succeeds());
+  free(tmpl);
+}
 
 TEST_F(LlvmLibcScandirTest, TestBadDirname) {
   struct dirent **namelist;

>From b57c15020f9a5204df17cf0628ed37a6cef90b52 Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Sat, 12 Sep 2026 18:07:20 -0700
Subject: [PATCH 14/15] Fix clang format errors

---
 .../llvm-libc-types/__scandir_compare_t.h     |  5 ++-
 .../llvm-libc-types/__scandir_filter_t.h      |  2 +-
 libc/src/dirent/linux/scandir.cpp             | 16 ++++---
 libc/test/src/dirent/scandir_test.cpp         | 44 ++++++++++---------
 4 files changed, 36 insertions(+), 31 deletions(-)

diff --git a/libc/include/llvm-libc-types/__scandir_compare_t.h b/libc/include/llvm-libc-types/__scandir_compare_t.h
index 0039188ee6470..615955d0c60c0 100644
--- a/libc/include/llvm-libc-types/__scandir_compare_t.h
+++ b/libc/include/llvm-libc-types/__scandir_compare_t.h
@@ -16,6 +16,7 @@
 
 #include "struct_dirent.h"
 
-typedef int (*__scandir_compare_t)(const struct dirent **, const struct dirent **);
+typedef int (*__scandir_compare_t)(const struct dirent **,
+                                   const struct dirent **);
 
-#endif  // LLVM_LIBC_TYPES___SCANDIR_COMPARE_T_H
+#endif // LLVM_LIBC_TYPES___SCANDIR_COMPARE_T_H
diff --git a/libc/include/llvm-libc-types/__scandir_filter_t.h b/libc/include/llvm-libc-types/__scandir_filter_t.h
index 204c950ee1e2c..3f6aa99889f42 100644
--- a/libc/include/llvm-libc-types/__scandir_filter_t.h
+++ b/libc/include/llvm-libc-types/__scandir_filter_t.h
@@ -18,4 +18,4 @@
 
 typedef int (*__scandir_filter_t)(const struct dirent *);
 
-#endif  // LLVM_LIBC_TYPES___SCANDIR_FILTER_H
+#endif // LLVM_LIBC_TYPES___SCANDIR_FILTER_H
diff --git a/libc/src/dirent/linux/scandir.cpp b/libc/src/dirent/linux/scandir.cpp
index 37fd06075b136..d14b53255a1c4 100644
--- a/libc/src/dirent/linux/scandir.cpp
+++ b/libc/src/dirent/linux/scandir.cpp
@@ -17,10 +17,10 @@
 #include "hdr/func/malloc.h"
 #include "hdr/types/size_t.h"
 #include "hdr/types/struct_dirent.h"
+#include "src/__support/CPP/vector.h"
 #include "src/__support/common.h"
-#include "src/__support/macros/config.h"
 #include "src/__support/libc_errno.h"
-#include "src/__support/CPP/vector.h"
+#include "src/__support/macros/config.h"
 #include "src/dirent/closedir.h"
 #include "src/dirent/opendir.h"
 #include "src/dirent/readdir.h"
@@ -40,7 +40,7 @@ LLVM_LIBC_FUNCTION(int, scandir,
   }
 
   int saved_errno = 0;
-  LIBC_NAMESPACE::cpp::vector<struct dirent*> entries;
+  LIBC_NAMESPACE::cpp::vector<struct dirent *> entries;
 
   while (true) {
     libc_errno = 0;
@@ -58,7 +58,8 @@ LLVM_LIBC_FUNCTION(int, scandir,
 
     // struct dirent contains an equivalent of flexible array memeber we
     // allocate with malloc and use d_reclen as size.
-    struct dirent *new_entry = static_cast<struct dirent*>(::malloc(entry->d_reclen));
+    struct dirent *new_entry =
+        static_cast<struct dirent *>(::malloc(entry->d_reclen));
     if (new_entry == nullptr) {
       saved_errno = ENOMEM;
     }
@@ -75,7 +76,7 @@ LLVM_LIBC_FUNCTION(int, scandir,
   // POSIX-defined error codes for scandir. So we ignore closedir's errno.
   LIBC_NAMESPACE::closedir(dir_fd);
 
-  struct dirent **result = static_cast<struct dirent**>(
+  struct dirent **result = static_cast<struct dirent **>(
       ::malloc(entries.size() * sizeof(struct dirent *)));
 
   if (result == nullptr) {
@@ -83,7 +84,7 @@ LLVM_LIBC_FUNCTION(int, scandir,
   }
 
   if (saved_errno != 0) {
-    for (struct dirent *entry: entries) {
+    for (struct dirent *entry : entries) {
       ::free(entry);
     }
     libc_errno = saved_errno;
@@ -96,7 +97,8 @@ LLVM_LIBC_FUNCTION(int, scandir,
       auto right = static_cast<const struct dirent **>(const_cast<void *>(b));
       return compare(left, right);
     };
-    internal::unstable_sort(entries.data(), entries.size(), sizeof(struct dirent*), cmp_fn);
+    internal::unstable_sort(entries.data(), entries.size(),
+                            sizeof(struct dirent *), cmp_fn);
   }
 
   for (size_t i = 0; i < entries.size(); ++i) {
diff --git a/libc/test/src/dirent/scandir_test.cpp b/libc/test/src/dirent/scandir_test.cpp
index 2acc0e3ed47c4..fa9b701f65a0e 100644
--- a/libc/test/src/dirent/scandir_test.cpp
+++ b/libc/test/src/dirent/scandir_test.cpp
@@ -15,8 +15,8 @@
 #include "src/__support/OSUtil/path.h"
 #include "src/dirent/scandir.h"
 #include "src/stdio/asprintf.h"
-#include "src/stdio/fopen.h"
 #include "src/stdio/fclose.h"
+#include "src/stdio/fopen.h"
 #include "src/stdio/remove.h"
 #include "src/stdlib/mkdtemp.h"
 #include "src/string/strcoll.h"
@@ -37,12 +37,15 @@ constexpr int ENTRIES_MIN = 2;
 
 char *join_path(char *dir, const char *filename) {
   char *path = nullptr;
-  if (LIBC_NAMESPACE::asprintf(&path, "%s%c%s", dir, LIBC_NAMESPACE::path::SEPARATOR, filename) == -1) {
+  if (LIBC_NAMESPACE::asprintf(&path, "%s%c%s", dir,
+                               LIBC_NAMESPACE::path::SEPARATOR,
+                               filename) == -1) {
     return nullptr;
   }
   return path;
 }
-bool create_empty_file(char *path) { FILE *file = LIBC_NAMESPACE::fopen(path, "w");
+bool create_empty_file(char *path) {
+  FILE *file = LIBC_NAMESPACE::fopen(path, "w");
   if (file == nullptr) {
     return false;
   }
@@ -53,14 +56,11 @@ bool create_empty_file(char *path) { FILE *file = LIBC_NAMESPACE::fopen(path, "w
   return true;
 }
 
-
 int alphasort(const struct dirent **a, const struct dirent **b) {
   return LIBC_NAMESPACE::strcoll((*a)->d_name, (*b)->d_name);
 }
 
-int skip_hidden(const struct dirent *entry) {
-    return entry->d_name[0] != '.';
-}
+int skip_hidden(const struct dirent *entry) { return entry->d_name[0] != '.'; }
 
 TEST_F(LlvmLibcScandirTest, TestEmptyDir) {
   char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path(TEMPLATE));
@@ -68,20 +68,19 @@ TEST_F(LlvmLibcScandirTest, TestEmptyDir) {
   ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl));
 
   struct dirent **namelist;
-  ASSERT_THAT(LIBC_NAMESPACE::scandir(tmpl, &namelist, nullptr, nullptr), Succeeds(ENTRIES_MIN));
+  ASSERT_THAT(LIBC_NAMESPACE::scandir(tmpl, &namelist, nullptr, nullptr),
+              Succeeds(ENTRIES_MIN));
   // Order of namelist is not guaranteed so we can't easily use ASSERT_STREQ
-  ASSERT_TRUE(
-      (LIBC_NAMESPACE::strncmp(namelist[0]->d_name, ".",  1) == 0 &&
-       LIBC_NAMESPACE::strncmp(namelist[1]->d_name, "..", 2) == 0) ||
-      (LIBC_NAMESPACE::strncmp(namelist[0]->d_name, "..", 2) == 0 &&
-       LIBC_NAMESPACE::strncmp(namelist[1]->d_name, ".",  1) == 0));
+  ASSERT_TRUE((LIBC_NAMESPACE::strncmp(namelist[0]->d_name, ".", 1) == 0 &&
+               LIBC_NAMESPACE::strncmp(namelist[1]->d_name, "..", 2) == 0) ||
+              (LIBC_NAMESPACE::strncmp(namelist[0]->d_name, "..", 2) == 0 &&
+               LIBC_NAMESPACE::strncmp(namelist[1]->d_name, ".", 1) == 0));
 
   // We also test that both orderings can't be true at the same time.
-  ASSERT_FALSE(
-      (LIBC_NAMESPACE::strncmp(namelist[0]->d_name, ".",  1) == 0 &&
-       LIBC_NAMESPACE::strncmp(namelist[1]->d_name, "..", 2) == 0) &&
-      (LIBC_NAMESPACE::strncmp(namelist[0]->d_name, "..", 2) == 0 &&
-       LIBC_NAMESPACE::strncmp(namelist[1]->d_name, ".",  1) == 0));
+  ASSERT_FALSE((LIBC_NAMESPACE::strncmp(namelist[0]->d_name, ".", 1) == 0 &&
+                LIBC_NAMESPACE::strncmp(namelist[1]->d_name, "..", 2) == 0) &&
+               (LIBC_NAMESPACE::strncmp(namelist[0]->d_name, "..", 2) == 0 &&
+                LIBC_NAMESPACE::strncmp(namelist[1]->d_name, ".", 1) == 0));
 
   ASSERT_THAT(LIBC_NAMESPACE::rmdir(tmpl), Succeeds());
   free(tmpl);
@@ -93,7 +92,8 @@ TEST_F(LlvmLibcScandirTest, TestDirFilter) {
   ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl));
 
   struct dirent **namelist;
-  ASSERT_THAT(LIBC_NAMESPACE::scandir(tmpl, &namelist, skip_hidden, nullptr), Succeeds(0));
+  ASSERT_THAT(LIBC_NAMESPACE::scandir(tmpl, &namelist, skip_hidden, nullptr),
+              Succeeds(0));
 
   ASSERT_THAT(LIBC_NAMESPACE::rmdir(tmpl), Succeeds());
   free(tmpl);
@@ -117,7 +117,8 @@ TEST_F(LlvmLibcScandirTest, TestDirSorted) {
   ASSERT_TRUE(create_empty_file(path_1));
 
   struct dirent **namelist;
-  ASSERT_THAT(LIBC_NAMESPACE::scandir(tmpl, &namelist, skip_hidden, alphasort), Succeeds(3));
+  ASSERT_THAT(LIBC_NAMESPACE::scandir(tmpl, &namelist, skip_hidden, alphasort),
+              Succeeds(3));
 
   ASSERT_STREQ(namelist[0]->d_name, "1");
   ASSERT_STREQ(namelist[1]->d_name, "a");
@@ -137,5 +138,6 @@ TEST_F(LlvmLibcScandirTest, TestDirSorted) {
 
 TEST_F(LlvmLibcScandirTest, TestBadDirname) {
   struct dirent **namelist;
-  ASSERT_THAT(LIBC_NAMESPACE::scandir("", &namelist, NULL, NULL), Fails(ENOENT, -1));
+  ASSERT_THAT(LIBC_NAMESPACE::scandir("", &namelist, NULL, NULL),
+              Fails(ENOENT, -1));
 }

>From d6ea5cf2f1227745c80a59b56ba68022cf5b0a61 Mon Sep 17 00:00:00 2001
From: Vadim Kotov <kotov at google.com>
Date: Wed, 16 Sep 2026 18:54:48 +0000
Subject: [PATCH 15/15] Add aarch64 and riscv entry points.

---
 libc/config/linux/aarch64/entrypoints.txt | 1 +
 libc/config/linux/riscv/entrypoints.txt   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index fd931e7a0a615..ba749d38e02fc 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1088,6 +1088,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.dirent.dirfd
     libc.src.dirent.opendir
     libc.src.dirent.readdir
+    libc.src.dirent.scandir
     libc.src.dirent.fdopendir
 
     # errno.h entrypoints
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 2525202d4ff10..fcd330084af16 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1303,6 +1303,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.dirent.dirfd
     libc.src.dirent.opendir
     libc.src.dirent.readdir
+    libc.src.dirent.scandir
     libc.src.dirent.fdopendir
 
     # pthread.h entrypoints



More information about the libc-commits mailing list