// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
 
+#define _GNU_SOURCE
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <fts.h>
+#include <ftw.h>
 #include <libgen.h>
 #include <mntent.h>
 #include <stdbool.h>
        return err;
 }
 
-int open_obj_pinned(char *path, bool quiet)
+int open_obj_pinned(const char *path, bool quiet)
 {
-       int fd;
+       char *pname;
+       int fd = -1;
+
+       pname = strdup(path);
+       if (!pname) {
+               if (!quiet)
+                       p_err("mem alloc failed");
+               goto out_ret;
+       }
 
-       fd = bpf_obj_get(path);
+       fd = bpf_obj_get(pname);
        if (fd < 0) {
                if (!quiet)
-                       p_err("bpf obj get (%s): %s", path,
-                             errno == EACCES && !is_bpffs(dirname(path)) ?
+                       p_err("bpf obj get (%s): %s", pname,
+                             errno == EACCES && !is_bpffs(dirname(pname)) ?
                            "directory not in bpf file system (bpffs)" :
                            strerror(errno));
-               return -1;
+               goto out_free;
        }
 
+out_free:
+       free(pname);
+out_ret:
        return fd;
 }
 
-int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
+int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type)
 {
        enum bpf_obj_type type;
        int fd;
        jsonw_end_array(json_wtr);
 }
 
+/* extra params for nftw cb */
+static struct pinned_obj_table *build_fn_table;
+static enum bpf_obj_type build_fn_type;
+
+static int do_build_table_cb(const char *fpath, const struct stat *sb,
+                            int typeflag, struct FTW *ftwbuf)
+{
+       struct bpf_prog_info pinned_info;
+       __u32 len = sizeof(pinned_info);
+       struct pinned_obj *obj_node;
+       enum bpf_obj_type objtype;
+       int fd, err = 0;
+
+       if (typeflag != FTW_F)
+               goto out_ret;
+
+       fd = open_obj_pinned(fpath, true);
+       if (fd < 0)
+               goto out_ret;
+
+       objtype = get_fd_type(fd);
+       if (objtype != build_fn_type)
+               goto out_close;
+
+       memset(&pinned_info, 0, sizeof(pinned_info));
+       if (bpf_obj_get_info_by_fd(fd, &pinned_info, &len))
+               goto out_close;
+
+       obj_node = calloc(1, sizeof(*obj_node));
+       if (!obj_node) {
+               err = -1;
+               goto out_close;
+       }
+
+       obj_node->id = pinned_info.id;
+       obj_node->path = strdup(fpath);
+       if (!obj_node->path) {
+               err = -1;
+               free(obj_node);
+               goto out_close;
+       }
+
+       hash_add(build_fn_table->table, &obj_node->hash, obj_node->id);
+out_close:
+       close(fd);
+out_ret:
+       return err;
+}
+
 int build_pinned_obj_table(struct pinned_obj_table *tab,
                           enum bpf_obj_type type)
 {
-       struct bpf_prog_info pinned_info = {};
-       struct pinned_obj *obj_node = NULL;
-       __u32 len = sizeof(pinned_info);
        struct mntent *mntent = NULL;
-       enum bpf_obj_type objtype;
        FILE *mntfile = NULL;
-       FTSENT *ftse = NULL;
-       FTS *fts = NULL;
-       int fd, err;
+       int flags = FTW_PHYS;
+       int nopenfd = 16;
+       int err = 0;
 
        mntfile = setmntent("/proc/mounts", "r");
        if (!mntfile)
                return -1;
 
+       build_fn_table = tab;
+       build_fn_type = type;
+
        while ((mntent = getmntent(mntfile))) {
-               char *path[] = { mntent->mnt_dir, NULL };
+               char *path = mntent->mnt_dir;
 
                if (strncmp(mntent->mnt_type, "bpf", 3) != 0)
                        continue;
-
-               fts = fts_open(path, 0, NULL);
-               if (!fts)
-                       continue;
-
-               while ((ftse = fts_read(fts))) {
-                       if (!(ftse->fts_info & FTS_F))
-                               continue;
-                       fd = open_obj_pinned(ftse->fts_path, true);
-                       if (fd < 0)
-                               continue;
-
-                       objtype = get_fd_type(fd);
-                       if (objtype != type) {
-                               close(fd);
-                               continue;
-                       }
-                       memset(&pinned_info, 0, sizeof(pinned_info));
-                       err = bpf_obj_get_info_by_fd(fd, &pinned_info, &len);
-                       if (err) {
-                               close(fd);
-                               continue;
-                       }
-
-                       obj_node = malloc(sizeof(*obj_node));
-                       if (!obj_node) {
-                               close(fd);
-                               fts_close(fts);
-                               fclose(mntfile);
-                               return -1;
-                       }
-
-                       memset(obj_node, 0, sizeof(*obj_node));
-                       obj_node->id = pinned_info.id;
-                       obj_node->path = strdup(ftse->fts_path);
-                       hash_add(tab->table, &obj_node->hash, obj_node->id);
-
-                       close(fd);
-               }
-               fts_close(fts);
+               err = nftw(path, do_build_table_cb, nopenfd, flags);
+               if (err)
+                       break;
        }
        fclose(mntfile);
-       return 0;
+       return err;
 }
 
 void delete_pinned_obj_table(struct pinned_obj_table *tab)