[RFC v2 i-g-t 2/5] lib/igt_dir: Directory processing and flexible file handling
Peter Senna Tschudin
peter.senna at linux.intel.com
Tue May 20 19:29:46 UTC 2025
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: marcin.bernatowicz at intel.com
Cc: himanshu.girotra at intel.com
Cc: aditya.chauhan at intel.com
Cc: pravalika.gurram at intel.com
Cc: sai.gowtham.ch at intel.com
Cc: ramadevi.gandi at intel.com
Cc: lucas.demarchi at intel.com
Cc: rodrigo.vivi at intel.com
Cc: kamil.konieczny at linux.intel.com
Cc: katarzyna.piecielska at intel.com
Cc: zbigniew.kempczynski at intel.com
Signed-off-by: Peter Senna Tschudin <peter.senna at linux.intel.com>
---
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..0e43b7e97
--- /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)) != NULL) {
+ 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 != NULL);
+ igt_require(config->root_path != NULL);
+ 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 != NULL);
+ igt_require(config->root_path != NULL);
+ igt_require(config->dirfd >= 0);
+
+ if (callback == NULL)
+ 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 != NULL);
+
+ 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 b58976a43..5742df0d8 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -90,6 +90,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
More information about the igt-dev
mailing list