From: Peter Senna Tschudin <peter.senna@linux.intel.com>
To: igt-dev@lists.freedesktop.org
Cc: Peter Senna Tschudin <peter.senna@linux.intel.com>,
michal.wajdeczko@intel.com, marcin.bernatowicz@intel.com,
kamil.konieczny@linux.intel.com, katarzyna.piecielska@intel.com,
zbigniew.kempczynski@intel.com, ewelina.musial@intel.com
Subject: [PATCH v3 resend i-g-t 1/6] lib/igt_dir: Directory processing and flexible file handling
Date: Mon, 16 Jun 2025 09:42:33 +0200 [thread overview]
Message-ID: <20250616074240.45818-2-peter.senna@linux.intel.com> (raw)
In-Reply-To: <20250616074240.45818-1-peter.senna@linux.intel.com>
This update introduces new utilities to facilitate reading and
processing files within a directory, giving test writers greater control
over file selection and processing.
For example, to read and discard all files from debugfs:
fd = drm_open_driver_master(DRIVER_ANY);
debugfs = igt_debugfs_dir(fd);
igt_dir = igt_dir_create(debugfs);
igt_dir_scan_dirfd(igt_dir, -1); // -1 means unlimited scan depth
igt_dir_process_files(igt_dir, NULL, NULL);
The igt_dir_scan_dirfd() function builds a linked list of files (using
igt_list), making it easy to add or remove specific files before
processing. If you only want to process a predetermined set of files,
you can skip the scan step and add the files directly to the list.
The last two parameters of igt_dir_process_files() specify a callback
function and user data. If the callback is NULL, a default “read and
discard” function is used.
Cc: michal.wajdeczko@intel.com
Cc: marcin.bernatowicz@intel.com
Cc: kamil.konieczny@linux.intel.com
Cc: katarzyna.piecielska@intel.com
Cc: zbigniew.kempczynski@intel.com
Cc: ewelina.musial@intel.com
Signed-off-by: Peter Senna Tschudin <peter.senna@linux.intel.com>
---
v3:
- unchanged from v2
v2:
- changed style of comparison to NULL
lib/igt_dir.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++++
lib/igt_dir.h | 61 ++++++++++++
lib/meson.build | 1 +
3 files changed, 322 insertions(+)
create mode 100644 lib/igt_dir.c
create mode 100644 lib/igt_dir.h
diff --git a/lib/igt_dir.c b/lib/igt_dir.c
new file mode 100644
index 000000000..8f5a25e35
--- /dev/null
+++ b/lib/igt_dir.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <dirent.h>
+#include <fcntl.h>
+
+#include "igt.h"
+#include "lib/igt_dir.h"
+
+/**
+ * igt_dir_get_fd_path: Get the path of a file descriptor
+ * @fd: file descriptor to get the path for
+ * @path: buffer to store the path
+ * @path_len: length of the buffer
+ *
+ * Returns: 0 on success, a negative error code on failure
+ */
+int igt_dir_get_fd_path(int fd, char *path, size_t path_len)
+{
+ ssize_t len;
+ char proc_path[64];
+
+ snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", fd);
+ len = readlink(proc_path, path, path_len - 1);
+ if (len == -1)
+ return -1;
+
+ path[path_len] = '\0';
+ return 0;
+}
+
+/**
+ * igt_dir_callback_read_discard: Default callback function for reading and
+ * discarding file contents
+ * @filename: Path to the file
+ * @callback_data: Optional pointer to user-defined data passed to the callback
+ *
+ * Returns: 0 on success, a negative error code on failure
+ */
+int igt_dir_callback_read_discard(const char *filename,
+ void *callback_data)
+{
+ int fd;
+ char buf[4096];
+ ssize_t bytes_read;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ igt_debug("Failed to open file %s\n", filename);
+ return -1;
+ }
+ bytes_read = read(fd, buf, sizeof(buf) - 1);
+ if (bytes_read < 0) {
+ igt_debug("Failed to read file %s\n", filename);
+ close(fd);
+ return -1;
+ }
+ buf[bytes_read] = '\0';
+ igt_debug("Read %zd bytes from file %s: %s\n", bytes_read,
+ filename, buf);
+ close(fd);
+ return 0;
+}
+
+/**
+ * igt_dir_create: Create a new igt_dir_t struct
+ * @dirfd: file descriptor of the root directory
+ *
+ * Returns: Pointer to the new igt_dir_t struct, or NULL on failure
+ */
+igt_dir_t *igt_dir_create(int dirfd)
+{
+ igt_dir_t *config;
+ size_t path_len = 512;
+ char path[path_len];
+
+ config = malloc(sizeof(igt_dir_t));
+ if (!config)
+ return NULL;
+
+ config->dirfd = dirfd;
+
+ igt_dir_get_fd_path(dirfd, path, path_len);
+ igt_require(path[0] != '\0');
+
+ config->root_path = malloc(path_len);
+ if (!config->root_path) {
+ free(config);
+ return NULL;
+ }
+
+ strncpy(config->root_path, path, path_len);
+
+ IGT_INIT_LIST_HEAD(&config->file_list_head);
+
+ config->callback = NULL;
+
+ return config;
+}
+
+static int _igt_dir_scan_dirfd(igt_dir_t *config, int scan_maxdepth,
+ int depth, const char *current_path)
+{
+ struct dirent *entry;
+ igt_dir_file_list_t *file_list_entry;
+ DIR *dirp;
+ int dirfd;
+ int ret = 0;
+
+ if (depth > scan_maxdepth && scan_maxdepth != -1)
+ return 0;
+
+ if (!current_path) {
+ igt_debug("Invalid current path\n");
+ return -1;
+ }
+
+ dirfd = open(current_path, O_RDONLY | O_DIRECTORY);
+ if (dirfd < 0) {
+ igt_debug("Failed to open directory %s\n", current_path);
+ return -1;
+ }
+
+ dirp = fdopendir(dirfd);
+ if (!dirp) {
+ igt_debug("Failed to fdopendir %s\n", current_path);
+ close(dirfd);
+ return -1;
+ }
+
+ while ((entry = readdir(dirp))) {
+ char entry_path[PATH_MAX];
+
+ if (strcmp(entry->d_name, ".") == 0 ||
+ strcmp(entry->d_name, "..") == 0)
+ continue;
+
+ snprintf(entry_path, sizeof(entry_path),
+ "%s/%s", current_path, entry->d_name);
+
+ if (entry->d_type == DT_DIR) {
+ ret = _igt_dir_scan_dirfd(config, scan_maxdepth,
+ depth + 1, entry_path);
+ if (ret)
+ break;
+ } else {
+ /* Compute path relative to the scan root */
+ const char *relative_path = entry_path +
+ strlen(config->root_path);
+ if (*relative_path == '/')
+ relative_path++; /* skip leading slash */
+
+ file_list_entry = malloc(sizeof(igt_dir_file_list_t));
+ if (!file_list_entry) {
+ igt_debug("Failed to allocate memory for file list entry\n");
+ continue;
+ }
+ file_list_entry->relative_path = strdup(relative_path);
+ file_list_entry->match = true;
+ igt_list_add(&file_list_entry->link,
+ &config->file_list_head);
+ }
+ }
+
+ closedir(dirp);
+ close(dirfd);
+ return ret;
+}
+
+/**
+ * igt_dir_scan_dirfd: Perform a directory scan based on config.
+ * @config: Pointer to the igt_dir struct
+ * @scan_maxdepth: Maximum depth to scan the directory. -1 means no limit
+ *
+ * Returns: 0 on success, a negative error code on failure
+ */
+int igt_dir_scan_dirfd(igt_dir_t *config, int scan_maxdepth)
+{
+ igt_require(config);
+ igt_require(config->root_path);
+ igt_require(config->dirfd >= 0);
+ igt_require(scan_maxdepth >= -1);
+ igt_require(scan_maxdepth != 0);
+
+ /* If the linked list is not empty, clean it first */
+ if (!igt_list_empty(&config->file_list_head)) {
+ igt_dir_file_list_t *file_list_entry, *tmp;
+
+ igt_list_for_each_entry_safe(file_list_entry, tmp,
+ &config->file_list_head, link) {
+ free(file_list_entry->relative_path);
+ free(file_list_entry);
+ }
+ }
+
+ return _igt_dir_scan_dirfd(config, scan_maxdepth, 0, config->root_path);
+}
+
+/**
+ * igt_dir_process_files: Process files in the directory
+ * @config: Pointer to the igt_dir struct
+ * @callback: Callback function to process each file
+ * @callback_data: Optional pointer to user-defined data passed to the callback
+ *
+ * Returns: 0 on success, a negative error code on failure
+ */
+int igt_dir_process_files(igt_dir_t *config,
+ igt_dir_file_callback callback,
+ void *callback_data)
+{
+ igt_dir_file_list_t *file_list_entry;
+ int ret = 0;
+
+ igt_require(config);
+ igt_require(config->root_path);
+ igt_require(config->dirfd >= 0);
+
+ if (!callback)
+ callback = igt_dir_callback_read_discard;
+
+ igt_list_for_each_entry(file_list_entry, &config->file_list_head, link) {
+ /* Only if match is true */
+ if (file_list_entry->match) {
+ char full_path[PATH_MAX];
+
+ snprintf(full_path, sizeof(full_path),
+ "%s/%s", config->root_path,
+ file_list_entry->relative_path);
+ ret = callback(full_path, callback_data);
+ if (ret)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * igt_dir_destroy: Destroy the igt_dir struct
+ * @config: Pointer to the igt_dir struct
+ *
+ * Returns: 0 on success, a negative error code on failure
+ */
+void igt_dir_destroy(igt_dir_t *config)
+{
+ igt_dir_file_list_t *file_list_entry, *tmp;
+
+ igt_require(config);
+
+ igt_list_for_each_entry_safe(file_list_entry, tmp,
+ &config->file_list_head, link) {
+ free(file_list_entry->relative_path);
+ free(file_list_entry);
+ }
+
+ free(config->root_path);
+ free(config);
+}
diff --git a/lib/igt_dir.h b/lib/igt_dir.h
new file mode 100644
index 000000000..fb9230862
--- /dev/null
+++ b/lib/igt_dir.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: MIT
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef IGT_DIR_H
+#define IGT_DIR_H
+
+#include "igt_list.h"
+
+/**
+ * Callback function type for processing files
+ * The callback is blocking, meaning traversal waits for it to return
+ * before proceeding to the next file
+ * @filename: Path to the file
+ * @callback_data: Optional pointer to user-defined data passed to the callback
+ *
+ * Returns:
+ * 0 on success, a negative error code on failure.
+ */
+typedef int (*igt_dir_file_callback)(const char *filename,
+ void *callback_data);
+
+/**
+ * igt_dir_file_list_t: List of files with a relative path
+ * @relative_path: path to a file, relative to the root directory
+ * @match: a boolean used to filter the list of files. When match=true the
+ * file is processed, otherwise it is skipped
+ * @link: list head for linking files in the list
+ */
+typedef struct {
+ char *relative_path;
+ bool match;
+ struct igt_list_head link;
+} igt_dir_file_list_t;
+
+/**
+ * igt_dir_t: Main struct for igt_dir
+ * @dirfd: file descriptor of the root directory
+ * @root_path: string of the root path, for example:
+ * /sys/kernel/debug/dri/0000:00:02.0/
+ * @file_list_head: head of the list of files
+ * @callback: Callback function for file operations. If NULL, defaults
+ * to reading and discarding file contents
+ */
+typedef struct {
+ int dirfd;
+ char *root_path;
+ struct igt_list_head file_list_head;
+ igt_dir_file_callback callback;
+} igt_dir_t;
+
+int igt_dir_get_fd_path(int fd, char *path, size_t path_len);
+int igt_dir_callback_read_discard(const char *filename,
+ void *callback_data);
+igt_dir_t *igt_dir_create(int dirfd);
+int igt_dir_scan_dirfd(igt_dir_t *config, int scan_maxdepth);
+int igt_dir_process_files(igt_dir_t *config,
+ igt_dir_file_callback callback,
+ void *callback_data);
+void igt_dir_destroy(igt_dir_t *config);
+#endif /* IGT_DIR_H */
diff --git a/lib/meson.build b/lib/meson.build
index ff81baae1..ec4a71bd7 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -91,6 +91,7 @@ lib_sources = [
'igt_kms.c',
'igt_fb.c',
'igt_core.c',
+ 'igt_dir.c',
'igt_draw.c',
'igt_list.c',
'igt_map.c',
--
2.43.0
next prev parent reply other threads:[~2025-06-16 7:43 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-16 7:42 [PATCH v3 resend i-g-t 0/6] Replace intel_sysfs_debugfs Peter Senna Tschudin
2025-06-16 7:42 ` Peter Senna Tschudin [this message]
2025-06-24 13:24 ` [PATCH v3 resend i-g-t 1/6] lib/igt_dir: Directory processing and flexible file handling Sokolowski, Jan
2025-06-16 7:42 ` [PATCH v3 resend i-g-t 2/6] tests: Add core_debugfs Peter Senna Tschudin
2025-06-16 7:42 ` [PATCH v3 resend i-g-t 3/6] tests: Add core_debugfs_heads_power Peter Senna Tschudin
2025-07-03 20:39 ` Rodrigo Vivi
2025-07-04 8:06 ` Peter Senna Tschudin
2025-07-04 10:01 ` Karthik B S
2025-07-04 11:36 ` Kamil Konieczny
2025-07-04 10:02 ` Karthik B S
2025-06-16 7:42 ` [PATCH v3 resend i-g-t 4/6] tests: Add core_sysfs Peter Senna Tschudin
2025-06-16 7:42 ` [PATCH v3 resend i-g-t 5/6] tests: Add xe_debugfs Peter Senna Tschudin
2025-07-03 20:36 ` Rodrigo Vivi
2025-07-04 8:57 ` Peter Senna Tschudin
2025-07-04 9:06 ` Peter Senna Tschudin
2025-07-07 14:43 ` Rodrigo Vivi
2025-06-16 7:42 ` [PATCH v3 resend i-g-t 6/6] tests/intel: Remove intel_sysfs_debugfs Peter Senna Tschudin
2025-07-03 20:39 ` Rodrigo Vivi
2025-06-16 19:50 ` ✓ Xe.CI.BAT: success for Replace intel_sysfs_debugfs Patchwork
2025-06-17 2:49 ` ✗ Xe.CI.Full: failure " Patchwork
2025-06-17 13:01 ` ✗ i915.CI.BAT: " Patchwork
2025-07-03 21:44 ` ✓ Xe.CI.BAT: success for Replace intel_sysfs_debugfs (rev2) Patchwork
2025-07-03 21:45 ` ✓ i915.CI.BAT: " Patchwork
2025-07-04 4:29 ` ✗ i915.CI.Full: failure " Patchwork
2025-07-05 14:40 ` ✓ Xe.CI.Full: success " Patchwork
2025-07-07 21:03 ` [PATCH v4 i-g-t 0/6] Replace intel_sysfs_debugfs Peter Senna Tschudin
2025-07-07 21:03 ` [PATCH v4 i-g-t 1/6] lib/igt_dir: Directory processing and flexible file handling Peter Senna Tschudin
2025-07-07 21:03 ` [PATCH v4 i-g-t 2/6] tests: Add core_debugfs Peter Senna Tschudin
2025-07-07 21:03 ` [PATCH v4 i-g-t 3/6] tests: Add kms_debugfs Peter Senna Tschudin
2025-07-07 21:03 ` [PATCH v4 i-g-t 4/6] tests: Add core_sysfs Peter Senna Tschudin
2025-07-07 21:03 ` [PATCH v4 i-g-t 5/6] tests: Add xe_debugfs Peter Senna Tschudin
2025-07-07 21:03 ` [PATCH v4 i-g-t 6/6] tests/intel: Remove intel_sysfs_debugfs Peter Senna Tschudin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250616074240.45818-2-peter.senna@linux.intel.com \
--to=peter.senna@linux.intel.com \
--cc=ewelina.musial@intel.com \
--cc=igt-dev@lists.freedesktop.org \
--cc=kamil.konieczny@linux.intel.com \
--cc=katarzyna.piecielska@intel.com \
--cc=marcin.bernatowicz@intel.com \
--cc=michal.wajdeczko@intel.com \
--cc=zbigniew.kempczynski@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.