From b2a4fab21c56028dfad31f4ca081e3a5da3c9809 Mon Sep 17 00:00:00 2001
From: Luis Gerhorst <privat@luisgerhorst.de>
Date: Tue, 13 Jul 2021 14:35:53 +0200
Subject: [PATCH] task_find: Add sys variant

---
 src/task_find.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 86 insertions(+), 4 deletions(-)

diff --git a/src/task_find.c b/src/task_find.c
index 110894a..f930b45 100644
--- a/src/task_find.c
+++ b/src/task_find.c
@@ -21,6 +21,12 @@
 /* isprint */
 #include <ctype.h>
 
+#include <sys/syscall.h> // SYS_getdents64
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+
 #include <sys/resource.h>
 #include <bpf/libbpf.h>
 #include "task_find.skel.h"
@@ -214,6 +220,82 @@ destroy:
 	return ret;
 }
 
+typedef uint64_t ino64_t;
+typedef uint64_t off64_t;
+
+struct linux_dirent64 {
+	ino64_t        d_ino;    /* 64-bit inode number */
+	off64_t        d_off;    /* 64-bit offset to next structure */
+	unsigned short d_reclen; /* Size of this dirent */
+	unsigned char  d_type;   /* File type */
+	char           d_name[]; /* Filename (null-terminated) */
+};
+
+/* Same as in GNU find. */
+#define BUF_SIZE (32*1024)
+
+static int do_user_find(unsigned char d_type, int parent_dfd, const char *name,
+			const char *search_name, int search_type, bool debug) {
+	int err = 0;
+	size_t namelen = strlen(name);
+
+	if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
+		return 0;
+	}
+
+	bool match =
+		search_type != -1 && d_type == search_type
+		|| search_name != NULL && strcmp(search_name, name) == 0;
+	if (match) {
+		write(STDOUT_FILENO, name, namelen);
+		write(STDOUT_FILENO, "\n", 1);
+	}
+
+	if (d_type != DT_DIR) {
+		return 0;
+	}
+	int dfd = openat(parent_dfd, name, O_RDONLY | O_DIRECTORY);
+	if (dfd == -1) {
+		perror("openat");
+		fprintf(stderr, "Warning: openat(%d, %s, ...) failed, is someone modifying the tree?\n", parent_dfd, name);
+		return 0;
+	}
+
+	write(STDOUT_FILENO, name, namelen);
+	write(STDOUT_FILENO, "/\n", 2);
+
+	while (true) {
+		char buf[BUF_SIZE];
+		int nread = syscall(SYS_getdents64, dfd, buf, BUF_SIZE);
+		if (nread == -1) {
+			perror("getdents64");
+			exit(EXIT_FAILURE);
+		}
+		if (nread == 0) {
+			break;
+		}
+
+		for (int bpos = 0; bpos < nread;) {
+			struct linux_dirent64 *d = (struct linux_dirent64 *) (buf + bpos);
+			err = do_user_find(d->d_type, dfd, d->d_name, search_name, search_type, debug);
+			if (err) {
+				goto close;
+			}
+			bpos += d->d_reclen;
+		}
+	}
+
+close:
+	write(STDOUT_FILENO, "..\n", 3);
+	close(dfd);
+	return err;
+}
+
+static int user_find(const char *path, const char *search_name,
+		     bool debug, int search_type) {
+	return do_user_find(DT_DIR, AT_FDCWD, path, search_name, search_type, debug);
+}
+
 int main(int argc, char **argv)
 {
 	bool debug = false;
@@ -295,12 +377,12 @@ int main(int argc, char **argv)
 			argv[0], variant_bpf, debug, name, path);
 	}
 
+	int ret;
 	if (variant_bpf) {
-		return bpf_find(path, name, debug, type);
+		ret = bpf_find(path, name, debug, type);
 	} else {
-		fprintf(stderr, "Only bpf variant supported.\n");
-		return EXIT_FAILURE;
+		ret = user_find(path, name, debug, type);
 	}
 
-	return EXIT_SUCCESS;
+	return ret;
 }
-- 
GitLab