OS Manuals

llapi_find(3)

llapi_find, llapi_find_with_cb - find files in a Lustre filesystem with extensive filtering capabilities

LLAPI_FIND(3) Lustre Library Functions LLAPI_FIND(3)

llapi_find, llapi_find_with_cb - find files in a Lustre filesystem with extensive filtering capabilities

#include <lustre/lustreapi.h>
int llapi_find(char *path, struct find_param *param);
int llapi_find_with_cb(char *path, struct find_param *param,
                       llapi_find_cb_t cb_init,
                       llapi_find_cb_t cb_fini);
typedef int (*llapi_find_cb_t)(char *path, int p, int *d,
                               struct find_param *param,
                               struct dirent64 *de);

The llapi_find() and llapi_find_with_cb() functions traverse a Lustre filesystem directory tree starting at path and identify files and directories matching criteria specified in the param structure. These functions provide programmatic access to file search capabilities similar to lfs find(1) command.

llapi_find() uses default callback functions for file matching and directory processing.

llapi_find_with_cb() allows custom callback functions to be specified:

Callback invoked for each file/directory entry during traversal. If NULL, uses the default cb_find_init() callback.
Callback invoked after processing a directory's contents. If NULL, uses the default cb_common_fini() callback.

The traversal algorithm varies depending on whether parallel processing is enabled:

Single-threaded Mode (fp_thread_count == 0 or 1):

Uses pure depth-first traversal via the internal llapi_semantic_traverse() function. For each directory:

1.
Call cb_init callback on the directory itself
2.
Read all directory entries
3.
For non-directory entries: call cb_init then cb_fini
4.
For subdirectories: recursively call llapi_semantic_traverse()
5.
Call cb_fini callback on the directory

Parallel Mode (fp_thread_count > 1):

Uses a work-stealing queue architecture with multiple worker threads. The traversal is not pure depth-first; instead, subdirectories are added to a shared work queue for parallel processing:

1.
Main thread initializes a find_work_queue with the starting path
2.
N worker threads are created (where N = fp_thread_count)
3.
Each worker thread:
  • Dequeues a work unit (directory) from the queue head
  • Calls llapi_semantic_traverse() on that directory
  • During traversal, when a subdirectory is encountered, instead of recursing immediately, a new work unit is created and added to the queue tail.
  • Non-directory files are processed immediately
  • Repeats until queue is empty and all work units complete
4.
Main thread waits for fwq_active_units to reach zero before returning

This work-stealing design provides load balancing: idle workers automatically pick up work from the shared queue, and directory trees are processed in parallel rather than sequentially.

Traversal Control Fields:

Maximum directory depth to traverse (0 = no limit).
Minimum directory depth before processing entries.
Enable recursive directory descent (default: enabled).
Number of parallel worker threads:
  • 0 or 1: Single-threaded depth-first traversal
  • >1: Parallel traversal with work-stealing queue
If set, stop entire traversal on first error. In parallel mode, sets fwq_shutdown flag to terminate all workers.

Both single-threaded and parallel modes use the internal llapi_semantic_traverse() function as the core traversal engine. This function:

Opens and processes a single directory:

1.
Opens the directory using open(2) with O_RDONLY|O_NDELAY|O_DIRECTORY flags
2.
Reopens with O_NOFOLLOW to detect foreign symlink directories
3.
Calls cb_init callback on the directory (if provided)
4.
Converts the file descriptor to DIR* using fdopendir(3)
5.
Iterates through directory entries using readdir(3)

For each directory entry:

  • Skips "." and ".." entries
  • Appends entry name to path buffer (with '/' separator)
  • For DT_DIR (directories):
  • In single-threaded mode: recursively calls llapi_semantic_traverse()
  • In parallel mode: calls work_unit_create_and_add() to enqueue for worker threads
  • For other entry types: calls cb_init then cb_fini callbacks
  • Removes entry name from path buffer (restores to parent directory)

After processing all entries:

  • Calls cb_fini callback on the directory itself
  • Closes directory and file descriptors

Path buffer management: The path buffer is modified in-place during traversal. The buffer must be sized at least 2 * PATH_MAX + 1 bytes to accommodate the deepest possible paths. Entry names are appended with '/' separators during descent and removed during ascent.

Depth tracking: The fp_depth field tracks current depth (0 = root). Before descending into a subdirectory, if fp_depth == fp_max_depth, the callback returns 1 to skip descent. After incrementing depth, if fp_depth < fp_min_depth, callbacks are skipped (but traversal continues).

File descriptor optimization: Uses openat(2) when parent directory file descriptor is available, avoiding full path resolution overhead. Keeps directory file descriptors open during traversal for efficient subdirectory access.

When fp_thread_count > 1, the parallel find uses a find_work_queue structure to coordinate worker threads:


struct find_work_queue {

struct find_work_unit *fwq_head; /* Dequeue from head */
struct find_work_unit *fwq_tail; /* Enqueue to tail */
pthread_mutex_t fwq_lock;
pthread_cond_t fwq_sleep_cond;
atomic_int fwq_active_units; /* Active work count */
bool fwq_shutdown; /* Shutdown flag */
int fwq_error; /* First error code */
llapi_find_cb_t fwq_cb_init; /* Init callback */
llapi_find_cb_t fwq_cb_fini; /* Fini callback */ }; struct find_work_unit {
struct find_work_unit *fwu_next;
struct find_param *fwu_param; /* Deep copy */
struct dirent64 *fwu_de;
char *fwu_path; };

Queue Operations:

When llapi_semantic_traverse() encounters a subdirectory (in parallel mode), it calls work_unit_create_and_add() which:
1.
Creates a work unit with a deep copy of find_param
2.
Increments fwq_active_units atomically
3.
Adds unit to queue tail (under fwq_lock)
4.
Signals fwq_sleep_cond to wake one waiting worker
Worker threads dequeue from the queue head:
1.
Acquire fwq_lock
2.
Wait on fwq_sleep_cond if queue is empty and not shutting down
3.
Remove unit from head, update fwq_head pointer
4.
Release fwq_lock
5.
Process the work unit
6.
Decrement fwq_active_units atomically
Idle workers automatically "steal" work from the shared queue. This provides dynamic load balancing without requiring work distribution logic.
Main thread polls fwq_active_units (using atomic read). When it reaches zero, all subdirectories have been processed and all workers are idle or waiting.
If any worker encounters an error and fp_stop_on_error is set:
  • Worker sets fwq_shutdown = true
  • Worker broadcasts fwq_sleep_cond to wake all workers
  • All workers exit their processing loops
  • Main thread detects shutdown and returns error

Custom callbacks must conform to the llapi_find_cb_t signature:

int callback(char *path, int p, int *d, struct find_param *param,

struct dirent64 *de);

Parameters:

Full path to the current file/directory being processed.
File descriptor of the parent directory (-1 if not available).
Pointer to file descriptor of current entry (NULL for non-directories).
Pointer to the find_param structure. The fp_cb_data field can be used to pass custom callback data.
Directory entry structure (NULL for the root path).

Return values from callbacks:

0
Continue traversal normally.
1
Skip descending into this directory (for directories only).
-1
Stop traversal completely.
Error code (negative values).

The find_param structure supports extensive matching conditions. All conditions must be satisfied for a file to match (logical AND). Fields must be initialized to 0 before use, then set as needed:

File Type and Name Matching:

File type to match (S_IFREG, S_IFDIR, S_IFLNK, etc.). Set fp_exclude_type to invert the match.
Filename pattern using fnmatch(3) syntax. Set fp_exclude_pattern to exclude matching names.

Owner and Permissions:

User ID to match. Set fp_check_uid to enable and fp_exclude_uid to invert.
Group ID to match. Set fp_check_gid to enable and fp_exclude_gid to invert.
Permission bits to match. Set fp_perm_sign to one of: LFS_FIND_PERM_EXACT (exact match), LFS_FIND_PERM_ALL (all bits set), or LFS_FIND_PERM_ANY (any bits set). Set fp_exclude_perm to invert.
Project ID to match. Set fp_check_projid to enable and fp_exclude_projid to invert.

Time-based Matching:

Access time to compare (seconds since epoch). Set fp_asign to -1 (older), 0 (exact), or 1 (newer). Set fp_exclude_atime to invert. fp_time_margin specifies tolerance in seconds.
Modification time. Same semantics as fp_atime. Use fp_msign and fp_exclude_mtime.
Change time. Same semantics as fp_atime. Use fp_csign and fp_exclude_ctime.
Birth/creation time. Same semantics as fp_atime. Use fp_bsign and fp_exclude_btime.
Enable --newerXY comparison. Uses fp_newery array for reference times indexed by NEWERXY_ATIME (0), NEWERXY_MTIME(1), NEWERXY_CTIME(2), NEWERXY_BTIME(3).

Size and Blocks:

File size in bytes. Set fp_check_size to enable, fp_size_sign to -1/0/1 (smaller/exact/larger), and fp_size_units for block size units. Set fp_exclude_size to invert.
Number of blocks. Set fp_check_blocks to enable, fp_blocks_sign to -1/0/1, and fp_blocks_units for units. Set fp_exclude_blocks to invert.

Lustre-specific Attributes:

Stripe size in bytes. Set fp_check_stripe_size to enable, fp_stripe_size_sign to -1/0/1, and fp_stripe_size_units for units. Set fp_exclude_stripe_size to invert.
Number of stripes. Set fp_check_stripe_count to enable, fp_stripe_count_sign to -1/0/1. Set fp_exclude_stripe_count to invert.
OST pool name. Set fp_check_pool to enable and fp_exclude_pool to invert.
Layout pattern (RAID0, MDT, etc.). Set fp_check_layout to enable and fp_exclude_layout to invert.
Component count for composite layouts. Set fp_check_comp_count to enable, fp_comp_count_sign to -1/0/1. Set fp_exclude_comp_count to invert.
Component flags to match. Set fp_check_comp_flags to enable. Use fp_comp_neg_flags for flags that must not be set.
Component start offset. Set fp_check_comp_start to enable, fp_comp_start_sign to -1/0/1, and fp_comp_start_units for units. Set fp_exclude_comp_start to invert.
Component end offset. Set fp_check_comp_end to enable, fp_comp_end_sign to -1/0/1, and fp_comp_end_units for units. Set fp_exclude_comp_end to invert.
Component ID to match. Set fp_check_comp_id to enable and fp_exclude_comp_id to invert.
Mirror count for FLR layouts. Set fp_check_mirror_count to enable, fp_mirror_count_sign to -1/0/1. Set fp_exclude_mirror_count to invert.
Mirror state to match. Set fp_check_mirror_state to enable. Use fp_mirror_neg_state for states that must not be set.
Mirror ID to match. Set fp_check_mirror_id to enable and fp_exclude_mirror_id to invert.
Mirror index to match. Set fp_check_mirror_index to enable, fp_mirror_index_sign to -1/0/1. Set fp_exclude_mirror_index to invert.
Extension size for PFL layouts. Set fp_check_ext_size to enable, fp_ext_size_sign to -1/0/1, and fp_ext_size_units for units. Set fp_exclude_ext_size to invert.

Target Selection:

Array of OST UUIDs to match. Set fp_num_obds to the number of UUIDs. Alternatively, use fp_obd_indexes array with OST indexes. Set fp_exclude_obd to find files NOT on these OSTs.
Array of MDT UUIDs to match. Set fp_num_mdts to the number of UUIDs. Alternatively, use fp_mdt_indexes array with MDT indexes. Set fp_exclude_mdt to find files NOT on these MDTs.
MDT count for striped directories. Set fp_check_mdt_count to enable, fp_mdt_count_sign to -1/0/1. Set fp_exclude_mdt_count to invert.
Hash type for striped directories.
Hash flags that must be set. Set fp_check_hash_flag to enable.
Hash flags that must not be set. Set fp_exclude_hash_type to enable.

Extended Attributes:

Extended attribute matching using regular expressions. Configure with xattr_match_info structure for name/value pattern matching.

File Attributes:

File attributes that must be set (statx attributes). Set fp_exclude_attrs to invert.
File attributes that must not be set.
Number of hard links. Set fp_nlink_sign to -1/0/1. Set fp_exclude_nlink to invert.

Foreign Files:

Foreign file/directory type. Set fp_check_foreign to match foreign files and fp_exclude_foreign to exclude them.

Compression (if supported):

Compression type. Set fp_check_compr_type to enable and fp_exclude_compr_type to invert.
Compression level. Set fp_check_compr_lvl to enable, fp_compr_lvl_sign to -1/0/1. Set fp_exclude_compr_lvl to invert.
Compression chunk size. Set fp_check_compr_chunk to enable, fp_compr_chunk_sign to -1/0/1. Set fp_exclude_compr_chunk to invert.

Output Control:

Verbosity level using llapi_layout_verbose enum values (VERBOSE_DEFAULT, VERBOSE_DETAIL, etc.).
Suppress output.
Custom printf-style format string for output (similar to lfs find --printf).
Output layout information in YAML format.

Performance and Behavior:

Use lazy size/block attributes when available.
Stop traversal on first error instead of continuing.
Percentage of entries to skip (for sampling).

0
on success.
<0
negative error code on failure and sets errno appropriately.

Invalid arguments (e.g., path too long, invalid parameters).
Insufficient memory for traversal or thread pool.
Starting path does not exist.
Permission denied accessing files or directories.
Path component is not a directory.
Operation not supported (e.g., btime on filesystems without support).

#include <lustre/lustreapi.h>
#include <stdio.h>
#include <string.h>
int main(void)
{

struct find_param param;
int rc;
/* Initialize to zero */
memset(&param, 0, sizeof(param));
/* Find all regular files larger than 1GB */
param.fp_type = S_IFREG;
param.fp_check_size = 1;
param.fp_size = 1ULL << 30; /* 1GB */
param.fp_size_sign = 1; /* larger than */
rc = llapi_find("/mnt/lustre", &param);
if (rc < 0)
fprintf(stderr, "llapi_find failed: %s\n",
strerror(-rc));
return rc; }

struct find_param param;
memset(&param, 0, sizeof(param));
/* Find files with stripe count >= 4 */
param.fp_check_stripe_count = 1;
param.fp_stripe_count = 4;
param.fp_stripe_count_sign = 1;  /* >= */
/* Modified in the last 24 hours */
param.fp_mtime = time(NULL) - 86400;
param.fp_msign = 1;  /* newer than */
llapi_find("/mnt/lustre/project", &param);

#include <lustre/lustreapi.h>
int my_callback(char *path, int p, int *d,

struct find_param *param,
struct dirent64 *de) {
/* Custom processing */
printf("Found: %s\n", path);
return 0; /* Continue traversal */ } int main(void) {
struct find_param param;
memset(&param, 0, sizeof(param));
param.fp_thread_count = 8; /* Use 8 threads */
param.fp_max_depth = 3; /* Max 3 levels deep */
llapi_find_with_cb("/mnt/lustre", &param,
my_callback, NULL);
return 0; }

•
Always initialize the find_param structure to zero before use.
•
The fp_thread_count field enables parallel traversal for improved performance on large directory trees. Set to 0 for single-threaded operation.
•
When using custom callbacks with llapi_find_with_cb(), ensure they are thread-safe if fp_thread_count > 1.
•
The fp_cb_data field can pass custom data to callbacks.
•
Sign fields (-1, 0, 1) indicate "less than", "equal to", or "greater than" comparisons.
•
Time comparisons use fp_time_margin for tolerance (in seconds).
•
The traversal is depth-first and respects fp_max_depth and fp_min_depth boundaries.

llapi_find() is part of the lustre(7) user application interface library.

llapi_find_with_cb() was added to support custom callbacks and parallel find operations.

lfs-find(1), llapi_getstripe(3), llapi_layout(7), lustreapi(7), fnmatch(3), stat(2), statx(2), open(2), openat(2), fdopendir(3), readdir(3), pthread_create(3), pthread_mutex_lock(3), pthread_cond_wait(3)

2026-07-03 Lustre User API

lustre 2.17.60-r0 · enterprise-packages · ingested 2026-10-22