[libc-commits] [libc] Nftw (PR #120969)

Jeff Bailey via libc-commits libc-commits at lists.llvm.org
Mon Dec 23 06:17:45 PST 2024


https://github.com/kaladron created https://github.com/llvm/llvm-project/pull/120969

None

>From 33312f1c705395c6b6b65f594291332365a4a7bb Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jeffbailey at google.com>
Date: Wed, 11 Sep 2024 09:21:08 +0000
Subject: [PATCH 1/7] Initial check-in of infrastructure to support ftw/nftw

---
 libc/config/linux/x86_64/entrypoints.txt |  5 ++++-
 libc/include/CMakeLists.txt              |  9 +++++++++
 libc/include/ftw.h.def                   | 16 ++++++++++++++++
 libc/newhdrgen/yaml/ftw.h                | 15 +++++++++++++++
 libc/src/CMakeLists.txt                  |  1 +
 libc/src/ftw/CMakeLists.txt              | 11 +++++++++++
 libc/src/ftw/ftw.cpp                     |  1 +
 libc/src/ftw/ftw.h                       | 20 ++++++++++++++++++++
 libc/test/src/CMakeLists.txt             |  1 +
 libc/test/src/ftw/CMakeLists.txt         | 11 +++++++++++
 libc/test/src/ftw/ftw_test.cpp           |  1 +
 11 files changed, 90 insertions(+), 1 deletion(-)
 create mode 100644 libc/include/ftw.h.def
 create mode 100644 libc/newhdrgen/yaml/ftw.h
 create mode 100644 libc/src/ftw/CMakeLists.txt
 create mode 100644 libc/src/ftw/ftw.cpp
 create mode 100644 libc/src/ftw/ftw.h
 create mode 100644 libc/test/src/ftw/CMakeLists.txt
 create mode 100644 libc/test/src/ftw/ftw_test.cpp

diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 3fd88fc0020e55..26226215b36394 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -32,6 +32,9 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.fcntl.open
     libc.src.fcntl.openat
 
+    # ftw.h entrypoints
+    libc.src.ftw.ftw
+
     # sched.h entrypoints
     libc.src.sched.sched_get_priority_max
     libc.src.sched.sched_get_priority_min
@@ -408,7 +411,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.fabs
     libc.src.math.fabsf
     libc.src.math.fabsl
-    libc.src.math.fadd 
+    libc.src.math.fadd
     libc.src.math.faddl
     libc.src.math.fadd
     libc.src.math.fdim
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index dfa5063889e8ab..c281d9d9f76c07 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -76,6 +76,15 @@ add_header_macro(
     .llvm_libc_common_h
 )
 
+add_header_macro(
+  ftw
+  ../libc/newhdrgen/yaml/ftw.yaml
+  ftw.h.def
+  ftw.h
+  DEPENDS
+    .llvm_libc_common_h
+)
+
 add_header_macro(
   dlfcn
   ../libc/newhdrgen/yaml/dlfcn.yaml
diff --git a/libc/include/ftw.h.def b/libc/include/ftw.h.def
new file mode 100644
index 00000000000000..af3be565653192
--- /dev/null
+++ b/libc/include/ftw.h.def
@@ -0,0 +1,16 @@
+//===-- C standard library header ftw.h -----------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_FTW_H
+#define LLVM_LIBC_FTW_H
+
+#include "__llvm-libc-common.h"
+
+%%public_api()
+
+#endif // LLVM_LIBC_FTW_H
diff --git a/libc/newhdrgen/yaml/ftw.h b/libc/newhdrgen/yaml/ftw.h
new file mode 100644
index 00000000000000..37b44834d56c35
--- /dev/null
+++ b/libc/newhdrgen/yaml/ftw.h
@@ -0,0 +1,15 @@
+header: fcntl.h
+macros: []
+types: []
+enums: []
+objects: []
+functions:
+  - name: ftw
+    standards:
+      - POSIX
+    return_type: int
+    arguments:
+      - type: const char *
+      - type: int
+      - type: int
+
diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt
index d554c12fb1ec89..3a3b19482dae3a 100644
--- a/libc/src/CMakeLists.txt
+++ b/libc/src/CMakeLists.txt
@@ -4,6 +4,7 @@ add_subdirectory(ctype)
 add_subdirectory(dlfcn)
 add_subdirectory(errno)
 add_subdirectory(fenv)
+add_subdirectory(ftw)
 add_subdirectory(inttypes)
 add_subdirectory(math)
 add_subdirectory(stdbit)
diff --git a/libc/src/ftw/CMakeLists.txt b/libc/src/ftw/CMakeLists.txt
new file mode 100644
index 00000000000000..a9e219b8bdb45a
--- /dev/null
+++ b/libc/src/ftw/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_entrypoint_object(
+  ftw
+  SRCS
+    ftw.cpp
+  HDRS
+    ftw.h
+  DEPENDS
+    libc.src.__support.FPUtil.fenv_impl
+  COMPILE_OPTIONS
+    -O2
+)
diff --git a/libc/src/ftw/ftw.cpp b/libc/src/ftw/ftw.cpp
new file mode 100644
index 00000000000000..44833b1b607bcc
--- /dev/null
+++ b/libc/src/ftw/ftw.cpp
@@ -0,0 +1 @@
+#error "I got here!"
diff --git a/libc/src/ftw/ftw.h b/libc/src/ftw/ftw.h
new file mode 100644
index 00000000000000..107bd97fc1a2e7
--- /dev/null
+++ b/libc/src/ftw/ftw.h
@@ -0,0 +1,20 @@
+//===-- Implementation header of ftw ----------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_FTW_FTW_H
+#define LLVM_LIBC_SRC_FTW_FTW_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int ftw(const char* dirpath, int noopenfd, int flags);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_FTW_FTW_H
diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index ddc6a5c7f6965f..9fc89f88492a15 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -50,6 +50,7 @@ add_subdirectory(__support)
 add_subdirectory(ctype)
 add_subdirectory(errno)
 add_subdirectory(fenv)
+add_subdirectory(ftw)
 add_subdirectory(math)
 add_subdirectory(search)
 add_subdirectory(stdbit)
diff --git a/libc/test/src/ftw/CMakeLists.txt b/libc/test/src/ftw/CMakeLists.txt
new file mode 100644
index 00000000000000..a2b655f6ff272b
--- /dev/null
+++ b/libc/test/src/ftw/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_custom_target(libc_ftw_unittests)
+
+add_libc_unittest(
+  ftw_test
+  SUITE
+    libc_ftw_unittests
+  SRCS
+    ftw_test.cpp
+  DEPENDS
+    libc.src.errno.errno
+)
diff --git a/libc/test/src/ftw/ftw_test.cpp b/libc/test/src/ftw/ftw_test.cpp
new file mode 100644
index 00000000000000..44833b1b607bcc
--- /dev/null
+++ b/libc/test/src/ftw/ftw_test.cpp
@@ -0,0 +1 @@
+#error "I got here!"

>From bc9626810e2eefde2dc93d723b4797e026d5a620 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jeffbailey at google.com>
Date: Thu, 12 Sep 2024 10:27:43 +0000
Subject: [PATCH 2/7] Move raman's code into the empty files.

This currently doesn't build - it's just in the right places to work on it more.
---
 libc/src/ftw/ftw.cpp           | 194 ++++++++++++++++++++++++++-
 libc/test/src/ftw/ftw_test.cpp | 237 ++++++++++++++++++++++++++++++++-
 2 files changed, 429 insertions(+), 2 deletions(-)

diff --git a/libc/src/ftw/ftw.cpp b/libc/src/ftw/ftw.cpp
index 44833b1b607bcc..97018a198516d7 100644
--- a/libc/src/ftw/ftw.cpp
+++ b/libc/src/ftw/ftw.cpp
@@ -1 +1,193 @@
-#error "I got here!"
+//
+// https://man7.org/linux/man-pages/man3/ftw.3.html
+//
+
+/*
+ * -----
+ * Build
+ * -----
+ * cd ..../google3/experimental/users/rtenneti/llvm_libc/
+ * make
+ *
+ * ------------
+ * How to Test:
+ * ------------
+ * make test
+ */
+
+#include "nftw.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stddef.h>  // size_t
+#include <stdint.h>  // int8_t, int16_t and int32_t
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstring>
+#include <filesystem>  // NOLINT
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <optional>
+#include <string_view>
+
+namespace {
+class Func {
+ public:
+  virtual int call(const char*, const struct stat*, int, struct FTW*) = 0;
+};
+
+using nftwFn = int (*)(const char* filePath, const struct stat* statBuf,
+                       int tFlag, struct FTW* ftwbuf);
+
+using ftwFn = int (*)(const char* filePath, const struct stat* statBuf,
+                      int tFlag);
+
+class NftwFunc : public Func {
+ public:
+  NftwFunc(nftwFn fn) : fn(fn) {}
+  virtual int call(const char* dirPath, const struct stat* statBuf, int tFlag,
+                   struct FTW* ftwBuf) override {
+    return fn(dirPath, statBuf, tFlag, ftwBuf);
+  }
+
+ private:
+  const nftwFn fn;
+};
+
+class FtwFunc : public Func {
+ public:
+  FtwFunc(ftwFn fn) : fn(fn) {}
+  virtual int call(const char* dirPath, const struct stat* statBuf, int tFlag,
+                   struct FTW*) override {
+    return fn(dirPath, statBuf, tFlag);
+  }
+
+ private:
+  const ftwFn fn;
+};
+
+int doMergedFtw(const std::string& dirPath, Func& fn, int fdLimit, int flags,
+                int level) {
+  // fdLimit specifies the maximum number of directories that ftw()
+  // will hold open simultaneously. When a directory is opened, fdLimit is
+  // decreased and if it becomes 0 or less, we won't open any more directories.
+  if (fdLimit <= 0) {
+    return 0;
+  }
+
+  // Determine the type of path that is passed.
+  int typeFlag;
+  struct stat statBuf;
+  if (flags & FTW_PHYS) {
+    if (lstat(dirPath.c_str(), &statBuf) < 0) return -1;
+  } else {
+    if (stat(dirPath.c_str(), &statBuf) < 0) {
+      if (!lstat(dirPath.c_str(), &statBuf)) {
+        typeFlag = FTW_SLN; /* Symbolic link pointing to a nonexistent file. */
+      } else if (errno != EACCES) {
+        /* stat failed with an errror that is not Permission denied */
+        return -1;
+      } else {
+        /* The probable cause for the failure is that the caller had read
+         * permission on  the parent directory, so that the filename fpath could
+         * be seen, but did not have execute permission on the directory.
+         */
+        typeFlag = FTW_NS;
+      }
+    }
+  }
+
+  if (S_ISDIR(statBuf.st_mode)) {
+    if (flags & FTW_DEPTH) {
+      typeFlag = FTW_DP; /* Directory, all subdirs have been visited. */
+    } else {
+      typeFlag = FTW_D; /* Directory. */
+    }
+  } else if (S_ISLNK(statBuf.st_mode)) {
+    if (flags & FTW_PHYS) {
+      typeFlag = FTW_SL; /* Symbolic link.  */
+    } else {
+      typeFlag = FTW_SLN; /* Symbolic link pointing to a nonexistent file. */
+    }
+  } else {
+    typeFlag = FTW_F; /* Regular file.  */
+  }
+
+  struct FTW ftwBuf;
+  // Find the base by finding the last slash.
+  std::size_t slash_found = dirPath.rfind("/");
+  if (slash_found != std::string::npos) {
+    ftwBuf.base = slash_found + 1;
+  }
+
+  ftwBuf.level = level;
+
+  // If the dirPath is a file, call the function on it and return.
+  if ((typeFlag == FTW_SL) || (typeFlag == FTW_F)) {
+    int returnValue = fn.call(dirPath.c_str(), &statBuf, typeFlag, &ftwBuf);
+    if (returnValue) {
+      return returnValue;
+    }
+    return 0;
+  }
+
+  // If FTW_DEPTH is not set, nftw() shall report any directory before reporting
+  // the files in that directory.
+  if (!(flags & FTW_DEPTH)) {
+    // Call the function on the directory.
+    int directory_fd = open(dirPath.c_str(), O_RDONLY);
+    if (directory_fd < 0 && errno == EACCES) {
+      typeFlag = FTW_DNR; /* Directory can't be read. */
+    }
+    close(directory_fd);
+
+    int returnValue = fn.call(dirPath.c_str(), &statBuf, typeFlag, &ftwBuf);
+    if (returnValue) {
+      return returnValue;
+    }
+  }
+
+  for (std::error_code ec; auto const& dir_entry :
+                           std::filesystem::directory_iterator(dirPath, ec)) {
+    if (ec) continue;
+    int returnValue =
+        doMergedFtw(dir_entry.path(), fn, fdLimit - 1, flags, ftwBuf.level + 1);
+    if (returnValue) {
+      return returnValue;
+    }
+  }
+
+  // If FTW_DEPTH is set, nftw() shall report all files in a directory before
+  // reporting the directory itself.
+  if (flags & FTW_DEPTH) {
+    // Call the function on the directory.
+    return fn.call(dirPath.c_str(), &statBuf, typeFlag, &ftwBuf);
+  }
+  return 0;
+}
+} // namespace
+
+int llvm_libc_nftw(const std::string& dirPath,
+                   int (*fn)(const char* filePath, const struct stat* statBuf,
+                             int tFlag, struct FTW* ftwbuf),
+                   int fdLimit, int flags) {
+  NftwFunc wrappedFn{fn};
+  return doMergedFtw(dirPath, wrappedFn, fdLimit, flags, 0);
+}
+
+int llvm_libc_ftw(const std::string &dirPath,
+                  int (*fn)(const char *filePath, const struct stat *statBuf,
+                            int tFlag),
+                  int fdLimit) {
+  return llvm_libc_nftw(dirPath, (int (*)())fn, fdLimit, FTW_PHYS, 0);
+}
diff --git a/libc/test/src/ftw/ftw_test.cpp b/libc/test/src/ftw/ftw_test.cpp
index 44833b1b607bcc..9b93e49cb36bce 100644
--- a/libc/test/src/ftw/ftw_test.cpp
+++ b/libc/test/src/ftw/ftw_test.cpp
@@ -1 +1,236 @@
-#error "I got here!"
+// Test code starts here
+
+//nftw test code:
+
+namespace fs = std::filesystem;
+
+class TemporaryDirectory {
+ public:
+  TemporaryDirectory() {
+    fs::path temp_path = fs::temp_directory_path();
+    std::string temp_path_prefix = temp_path.string() + "/tmpdir.XXXXXX";
+    // Use data() to get a writable string. mkdtemp doesn't write beyond the
+    // allocated data.
+    char* dir_name = mkdtemp(temp_path_prefix.data());
+    _path = dir_name;
+    fs::current_path(_path);
+  }
+
+  ~TemporaryDirectory() {
+    fs::current_path(_path);
+    fs::remove_all(_path);
+  }
+  const std::string GetDirectoryPath() { return _path.c_str(); }
+
+ private:
+  fs::path _path;
+};
+
+static void setupTestData() {
+  fs::create_directories("sandbox");
+  fs::create_directory("sandbox/owner_all_group_read_others_read_dir");
+  fs::permissions("sandbox/owner_all_group_read_others_read_dir",
+                  fs::perms::owner_all | fs::perms::group_read |
+                      fs::perms::group_exec | fs::perms::others_read |
+                      fs::perms::others_exec,
+                  fs::perm_options::add);
+  fs::create_directory(
+      "sandbox/owner_all_group_read_others_read_dir/"
+      "owner_read_group_read_others_read_dir");
+  fs::permissions(
+      "sandbox/owner_all_group_read_others_read_dir/"
+      "owner_read_group_read_others_read_dir",
+      fs::perms::owner_read | fs::perms::owner_exec | fs::perms::group_read |
+          fs::perms::group_exec | fs::perms::others_read |
+          fs::perms::others_exec,
+      fs::perm_options::add);
+  fs::create_directory("sandbox/no_perm_dir");
+  fs::permissions("sandbox/no_perm_dir", fs::perms::none,
+                  fs::perm_options::add);
+
+  fs::create_symlink("invalid_target", "sandbox/sym1");
+  fs::create_directory_symlink("owner_all_group_read_others_read_dir",
+                               "sandbox/sym2");
+
+  std::ofstream ofs("sandbox/file");  // create regular file
+}
+
+static bool isReadable(const fs::path& p) {
+  std::error_code ec;  // For noexcept overload usage.
+  auto perms = fs::status(p, ec).permissions();
+  if ((perms & fs::perms::owner_read) != fs::perms::none &&
+      (perms & fs::perms::group_read) != fs::perms::none &&
+      (perms & fs::perms::others_read) != fs::perms::none) {
+    return true;
+  }
+  return false;
+}
+
+static int checkNftw(const char* arg_fpath, const struct stat* statBuf,
+                     int typeFlag, struct FTW* ftwbuf) {
+  displayInfo(arg_fpath, statBuf, typeFlag, ftwbuf);
+  if (arg_fpath == NULL) {
+    std::cout << " fpath is null\n";
+    return -1;
+  }
+  std::string fpath = arg_fpath;
+  if (statBuf == NULL) {
+    std::cout << " stat is null " << fpath << "\n";
+    return -1;
+  }
+
+  const fs::path path = fpath;
+
+  // status says we don't know the status of this file.
+  if (typeFlag == FTW_NS || typeFlag == FTW_SLN) {
+    struct stat sb;
+    // Verify we can't stat the path.
+    if (-1 != stat(arg_fpath, &sb)) {
+      std::cout << "status doesn't match for " << arg_fpath << "\n";
+      return -1;
+    }
+    return 0;
+  }
+
+  // If it is directory
+  if (S_ISDIR(statBuf->st_mode)) {
+    if (!fs::is_directory(fs::status(path))) {
+      std::cout << "Is not directory> " << fpath << "\n";
+      return -1;
+    }
+    if (isReadable(fpath)) {
+      // It is readable, verify typeFlag is correct.
+      if (typeFlag != FTW_D && typeFlag != FTW_DP) {
+        std::cout << "typeFlag != FTW_D && typeFlag != FTW_DP " << fpath
+                  << "\n";
+        return -1;
+      }
+      return 0;
+    }
+    // It is not readable, and verify typeFlag is correct.
+    if (typeFlag != FTW_DNR && typeFlag != FTW_D) {
+      std::cout << "typeFlag != FTW_DNR && typeFlag != FTW_D " << fpath << "\n";
+      return -1;
+    }
+    return 0;
+  }
+
+  // If it symlink, verify the filestatus and verify typeFlag is correct.
+  if (S_ISLNK(statBuf->st_mode)) {
+    if (!fs::is_symlink(fs::status(path))) {
+      std::cout << "Is not symlink" << fpath << "\n";
+      return -1;
+    }
+    if (FTW_SL != typeFlag) {
+      std::cout << " FTW_SL != typeFlag " << fpath << "\n";
+      return -1;
+    }
+    return 0;
+  }
+
+  if (!fs::is_regular_file(fs::status(path))) {
+    std::cout << " is not a regular file " << fpath << "\n";
+    return -1;
+  }
+  if (FTW_F != typeFlag) {
+    std::cout << " FTW_SL != typeFlag " << fpath << "\n";
+    return -1;
+  }
+  return 0; /* To tell llvm_libc_nftw() to continue */
+}
+
+static void testNftw() {
+  std::cout << std::endl << "Calling testNftw: " << std::endl;
+  TemporaryDirectory tmpDir = TemporaryDirectory();
+  setupTestData();
+  int flags = 0;
+  llvm_libc_nftw(tmpDir.GetDirectoryPath(), checkNftw, 128, flags);
+  std::cout << "All testNftw tests have passed: " << std::endl;
+}
+
+int main(int argc, char* argv[]) {
+  std::cout << "ftw called with args: " << argv[1] << std::endl;
+
+  int flags = 0;
+
+  if (argc > 2) {
+    if (strchr(argv[2], 'p') != NULL) flags |= FTW_PHYS;
+    if (strchr(argv[2], 'd') != NULL) flags |= FTW_DEPTH;
+  } else {
+    flags |= FTW_DEPTH;
+  }
+
+  std::cout << "Calling nftw: " << std::endl;
+  if (nftw((argc < 2) ? "." : argv[1], displayInfo, 20, flags) == -1) {
+    perror("nftw");
+    exit(EXIT_FAILURE);
+  }
+
+  std::cout << "Calling llvm_libc_nftw: " << std::endl;
+  std::string_view dirPath(argv[1]);
+  if (llvm_libc_nftw((argc < 2) ? "." : argv[1], displayInfo, 20, flags) ==
+      -1) {
+    perror("llvm_libc_nftw");
+    exit(EXIT_FAILURE);
+  }
+
+  // Unit tests for FTW.
+  testNftw();
+
+  exit(EXIT_SUCCESS);
+}
+
+// ftw test code:
+
+
+static int display_info(const char *filePath, const struct stat *sb, int tflag,
+                        struct FTW *ftwbuf) {
+  printf("%-3s %2d ",
+         (tflag == FTW_D)     ? "d"
+         : (tflag == FTW_DNR) ? "dnr"
+         : (tflag == FTW_DP)  ? "dp"
+         : (tflag == FTW_F)   ? "f"
+         : (tflag == FTW_NS)  ? "ns"
+         : (tflag == FTW_SL)  ? "sl"
+         : (tflag == FTW_SLN) ? "sln"
+                              : "???",
+         ftwbuf->level);
+
+  if (tflag == FTW_NS)
+    printf("-------");
+  else
+    printf("%7jd", (intmax_t)sb->st_size);
+
+  printf("   %-40s %d %s\n", filePath, ftwbuf->base, filePath + ftwbuf->base);
+
+  return 0; /* To tell llvm_libc_nftw() to continue */
+}
+
+int main(int argc, char *argv[]) {
+  std::cout << "ftw called with args: " << argv[1] << std::endl;
+
+  int flags = 0;
+
+  if (argc > 2) {
+    if (strchr(argv[2], 'p') != NULL) flags |= FTW_PHYS;
+    if (strchr(argv[2], 'd') != NULL) flags |= FTW_DEPTH;
+  } else {
+    flags |= FTW_DEPTH;
+  }
+
+  std::cout << "Calling nftw: " << std::endl;
+  if (ftw((argc < 2) ? "." : argv[1], display_info, 20) == -1) {
+    perror("nftw");
+    exit(EXIT_FAILURE);
+  }
+
+  std::cout << "Calling llvm_libc_nftw: " << std::endl;
+  std::string_view dirPath(argv[1]);
+  if (llvm_libc_ftw((argc < 2) ? "." : argv[1], display_info, 20) == -1) {
+    perror("llvm_libc_nftw");
+    exit(EXIT_FAILURE);
+  }
+
+  exit(EXIT_SUCCESS);
+}
+

>From 975b3a1a2cbd6d7a1fa36232a0761a68752aef73 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jeffbailey at google.com>
Date: Fri, 13 Sep 2024 09:55:26 +0000
Subject: [PATCH 3/7] Fix comment header, replace includes with llvm libc
 includes.  Fix namespace for some things (we don't use std::)   Add virtual
 destructors for Func.  Fixup ftw to call domerged directly.

---
 libc/src/ftw/ftw.cpp | 82 +++++++++++++++++---------------------------
 1 file changed, 32 insertions(+), 50 deletions(-)

diff --git a/libc/src/ftw/ftw.cpp b/libc/src/ftw/ftw.cpp
index 97018a198516d7..626acd6d2105a7 100644
--- a/libc/src/ftw/ftw.cpp
+++ b/libc/src/ftw/ftw.cpp
@@ -1,49 +1,26 @@
+//===-- Implementation of ftw function ------------------------------------===//
 //
-// https://man7.org/linux/man-pages/man3/ftw.3.html
+// 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
 //
+//===----------------------------------------------------------------------===//
 
-/*
- * -----
- * Build
- * -----
- * cd ..../google3/experimental/users/rtenneti/llvm_libc/
- * make
- *
- * ------------
- * How to Test:
- * ------------
- * make test
- */
-
-#include "nftw.h"
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <pthread.h>
-#include <stddef.h>  // size_t
-#include <stdint.h>  // int8_t, int16_t and int32_t
-#include <stdio.h>
-#include <string.h>
-#include <sys/socket.h>
+#include "src/ftw/ftw.h"
+
+#include "src/__support/common.h"
+#include "src/__support/CPP/string.h"
+#include "src/errno/libc_errno.h"
+
+#include <stddef.h>
 #include <sys/stat.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <cstdio>
-#include <cstring>
-#include <filesystem>  // NOLINT
-#include <filesystem>
-#include <fstream>
-#include <iostream>
-#include <optional>
-#include <string_view>
-
-namespace {
+
+
+namespace LIBC_NAMESPACE_DECL {
 class Func {
  public:
   virtual int call(const char*, const struct stat*, int, struct FTW*) = 0;
+  virtual ~Func() = 0;
 };
 
 using nftwFn = int (*)(const char* filePath, const struct stat* statBuf,
@@ -59,6 +36,7 @@ class NftwFunc : public Func {
                    struct FTW* ftwBuf) override {
     return fn(dirPath, statBuf, tFlag, ftwBuf);
   }
+  virtual ~NftwFunc() {}
 
  private:
   const nftwFn fn;
@@ -71,12 +49,13 @@ class FtwFunc : public Func {
                    struct FTW*) override {
     return fn(dirPath, statBuf, tFlag);
   }
+  virtual ~FtwFunc() {}
 
  private:
   const ftwFn fn;
 };
 
-int doMergedFtw(const std::string& dirPath, Func& fn, int fdLimit, int flags,
+int doMergedFtw(const cpp::string& dirPath, Func& fn, int fdLimit, int flags,
                 int level) {
   // fdLimit specifies the maximum number of directories that ftw()
   // will hold open simultaneously. When a directory is opened, fdLimit is
@@ -125,8 +104,8 @@ int doMergedFtw(const std::string& dirPath, Func& fn, int fdLimit, int flags,
 
   struct FTW ftwBuf;
   // Find the base by finding the last slash.
-  std::size_t slash_found = dirPath.rfind("/");
-  if (slash_found != std::string::npos) {
+  size_t slash_found = dirPath.rfind("/");
+  if (slash_found != cpp::string::npos) {
     ftwBuf.base = slash_found + 1;
   }
 
@@ -175,19 +154,22 @@ int doMergedFtw(const std::string& dirPath, Func& fn, int fdLimit, int flags,
   }
   return 0;
 }
-} // namespace
 
-int llvm_libc_nftw(const std::string& dirPath,
-                   int (*fn)(const char* filePath, const struct stat* statBuf,
-                             int tFlag, struct FTW* ftwbuf),
-                   int fdLimit, int flags) {
+LLVM_LIBC_FUNCTION(int, nftw, (const char *dirPath,
+                   int (*fn)(const char *filePath, const struct stat *statBuf,
+                             int tFlag, struct FTW *ftwbuf),
+                   int fdLimit, int flags)) {
   NftwFunc wrappedFn{fn};
   return doMergedFtw(dirPath, wrappedFn, fdLimit, flags, 0);
 }
 
-int llvm_libc_ftw(const std::string &dirPath,
+LLVM_LIBC_FUNCTION(int, ftw, (const char *dirPath,
                   int (*fn)(const char *filePath, const struct stat *statBuf,
                             int tFlag),
-                  int fdLimit) {
-  return llvm_libc_nftw(dirPath, (int (*)())fn, fdLimit, FTW_PHYS, 0);
+                  int fdLimit)) {
+  FtwFunc wrappedFn{fn};
+  return doMergedFtw(dirPath, wrappedFn, fdLimit, FTW_PHYS, 0);
 }
+
+} // namespace LIBC_NAMESPACE_DECL
+

>From 7e3a80e5598b82b2168e4ddd46f35291eefad965 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jeffbailey at google.com>
Date: Mon, 16 Sep 2024 10:04:52 +0000
Subject: [PATCH 4/7] [libc][nfc] Fix typo in header generation message.

---
 libc/cmake/modules/LLVMLibCHeaderRules.cmake | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libc/cmake/modules/LLVMLibCHeaderRules.cmake b/libc/cmake/modules/LLVMLibCHeaderRules.cmake
index c2c675bda26d31..76c4e1f2d3244c 100644
--- a/libc/cmake/modules/LLVMLibCHeaderRules.cmake
+++ b/libc/cmake/modules/LLVMLibCHeaderRules.cmake
@@ -118,7 +118,7 @@ function(add_gen_header2 target_name)
             ${entry_points}
             --output_dir ${out_file}
     DEPENDS ${yaml_file} ${def_file} ${fq_data_files}
-    COMMENT "Generating header ${ADD_GEN_HDR2_GE2N_HDR} from ${yaml_file} and ${def_file}"
+    COMMENT "Generating header ${ADD_GEN_HDR2_GEN_HDR} from ${yaml_file} and ${def_file}"
   )
   if(LIBC_TARGET_OS_IS_GPU)
     file(MAKE_DIRECTORY ${LIBC_INCLUDE_DIR}/llvm-libc-decls)
@@ -135,7 +135,7 @@ function(add_gen_header2 target_name)
       DEPENDS ${yaml_file} ${fq_data_files}
     )
   endif()
-  
+
   if(ADD_GEN_HDR2_DEPENDS)
     get_fq_deps_list(fq_deps_list ${ADD_GEN_HDR2_DEPENDS})
     # Dependencies of a add_header target can only be another add_gen_header target

>From 94f7ca8e54988470d2c8ed335db9cde317b55f12 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jeffbailey at google.com>
Date: Mon, 16 Sep 2024 10:09:38 +0000
Subject: [PATCH 5/7] Checkpoint in-progress header work

---
 libc/config/linux/api.td     |  3 +++
 libc/newhdrgen/yaml/ftw.yaml | 15 +++++++++++++++
 libc/spec/posix.td           | 32 ++++++++++++++++++++++++++++++++
 3 files changed, 50 insertions(+)
 create mode 100644 libc/newhdrgen/yaml/ftw.yaml

diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index 6a7c64296bf922..80b4da9e404a5d 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -19,6 +19,9 @@ def FCntlAPI : PublicAPI<"fcntl.h"> {
   ];
 }
 
+def FTWAPI : PublicAPI<"ftw.h"> {
+}
+
 def IntTypesAPI : PublicAPI<"inttypes.h"> {
   let Types = ["imaxdiv_t"];
 }
diff --git a/libc/newhdrgen/yaml/ftw.yaml b/libc/newhdrgen/yaml/ftw.yaml
new file mode 100644
index 00000000000000..37b44834d56c35
--- /dev/null
+++ b/libc/newhdrgen/yaml/ftw.yaml
@@ -0,0 +1,15 @@
+header: fcntl.h
+macros: []
+types: []
+enums: []
+objects: []
+functions:
+  - name: ftw
+    standards:
+      - POSIX
+    return_type: int
+    arguments:
+      - type: const char *
+      - type: int
+      - type: int
+
diff --git a/libc/spec/posix.td b/libc/spec/posix.td
index 085f2ec34ab346..85a1372bde2288 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -291,6 +291,37 @@ def POSIX : StandardSpec<"POSIX"> {
     ]
   >;
 
+  HeaderSpec FTW = HeaderSpec<
+    "ftw.h",
+    [], // Macros
+    [], // Types
+    [
+      EnumeratedNameValue<"FTW_CHDIR">,
+      EnumeratedNameValue<"FTW_DEPTH">,
+      EnumeratedNameValue<"FTW_MOUNT">,
+      EnumeratedNameValue<"FTW_PHYS">,
+      EnumeratedNameValue<"FTW_F">,
+      EnumeratedNameValue<"FTW_D">,
+      EnumeratedNameValue<"FTW_DP">,
+      EnumeratedNameValue<"FTW_SL">,
+      EnumeratedNameValue<"FTW_SLN">,
+      EnumeratedNameValue<"FTW_DNR">,
+      EnumeratedNameValue<"FTW_SLN">
+    ], // Enumerations
+    [
+      FunctionSpec<
+        "ftw",
+        RetValSpec<IntType>,
+        [ArgSpec<ConstCharPtr>]
+      >,
+      FunctionSpec<
+        "nftw",
+        RetValSpec<IntType>,
+        [ArgSpec<ConstCharPtr>]
+      >
+    ]
+  >;
+
   HeaderSpec SysMMan = HeaderSpec<
       "sys/mman.h",
       [
@@ -1771,6 +1802,7 @@ def POSIX : StandardSpec<"POSIX"> {
     DlFcn,
     Errno,
     FCntl,
+    FTW,
     PThread,
     Sched,
     Signal,

>From ebdab6b2273d6f9c02ef70d3086dbfc5dde4a57a Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jeffbailey at google.com>
Date: Wed, 18 Sep 2024 13:20:19 +0000
Subject: [PATCH 6/7] Add some enums for ftw headers.

---
 libc/include/ftw.h.def | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/libc/include/ftw.h.def b/libc/include/ftw.h.def
index af3be565653192..146de47de9191f 100644
--- a/libc/include/ftw.h.def
+++ b/libc/include/ftw.h.def
@@ -11,6 +11,33 @@
 
 #include "__llvm-libc-common.h"
 
+/* macros needed */
+
+enum {
+  FTW_D,
+  FTW_DNR,
+  FTW_F,
+  FTW_DP,
+  FTW_SL,
+  FTW_NS,
+  FTW_SLN
+}; // typeflag
+
+enum {
+  FTW_ACTIONRETVAL,
+  FTW_CHDIR,
+  FTW_DEPTH,
+  FTW_MOUNT,
+  FTW_PHYS
+}; // flags
+
+enum {
+  FTW_CONTINUE,
+  FTW_SKIP_SIBLINGS,
+  FTW_SKIP_SUBTREE,
+  FTW_STOP
+}; /* fn return */
+
 %%public_api()
 
 #endif // LLVM_LIBC_FTW_H

>From af4e30c15e536a465e6c98c578f9b7d146e38837 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jeffbailey at google.com>
Date: Mon, 30 Sep 2024 16:45:34 +0000
Subject: [PATCH 7/7] Fix errno, open and close.

---
 libc/src/ftw/CMakeLists.txt | 7 +++++--
 libc/src/ftw/ftw.cpp        | 7 +++++--
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/libc/src/ftw/CMakeLists.txt b/libc/src/ftw/CMakeLists.txt
index a9e219b8bdb45a..e0a45d6ee0fb97 100644
--- a/libc/src/ftw/CMakeLists.txt
+++ b/libc/src/ftw/CMakeLists.txt
@@ -5,7 +5,10 @@ add_entrypoint_object(
   HDRS
     ftw.h
   DEPENDS
+    libc.include.ftw
     libc.src.__support.FPUtil.fenv_impl
-  COMPILE_OPTIONS
-    -O2
+    libc.src.errno.errno
+    libc.src.fcntl.open
+    libc.src.unistd.close
+
 )
diff --git a/libc/src/ftw/ftw.cpp b/libc/src/ftw/ftw.cpp
index 626acd6d2105a7..dabd9b2c864458 100644
--- a/libc/src/ftw/ftw.cpp
+++ b/libc/src/ftw/ftw.cpp
@@ -11,7 +11,10 @@
 #include "src/__support/common.h"
 #include "src/__support/CPP/string.h"
 #include "src/errno/libc_errno.h"
+#include "src/fcntl/open.h"
+#include "src/unistd/close.h"
 
+#include <ftw.h>
 #include <stddef.h>
 #include <sys/stat.h>
 
@@ -73,7 +76,7 @@ int doMergedFtw(const cpp::string& dirPath, Func& fn, int fdLimit, int flags,
     if (stat(dirPath.c_str(), &statBuf) < 0) {
       if (!lstat(dirPath.c_str(), &statBuf)) {
         typeFlag = FTW_SLN; /* Symbolic link pointing to a nonexistent file. */
-      } else if (errno != EACCES) {
+      } else if (libc_errno != EACCES) {
         /* stat failed with an errror that is not Permission denied */
         return -1;
       } else {
@@ -125,7 +128,7 @@ int doMergedFtw(const cpp::string& dirPath, Func& fn, int fdLimit, int flags,
   if (!(flags & FTW_DEPTH)) {
     // Call the function on the directory.
     int directory_fd = open(dirPath.c_str(), O_RDONLY);
-    if (directory_fd < 0 && errno == EACCES) {
+    if (directory_fd < 0 && libc_errno == EACCES) {
       typeFlag = FTW_DNR; /* Directory can't be read. */
     }
     close(directory_fd);



More information about the libc-commits mailing list