LLAPI\_FIND(3) Lustre Library Functions LLAPI\_FIND(3)

# [NAME](#NAME)

llapi\_find, llapi\_find\_with\_cb - find files in a Lustre filesystem with extensive filtering capabilities

# [SYNOPSIS](#SYNOPSIS)

```
#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);
```

# [DESCRIPTION](#DESCRIPTION)

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:

[*cb\_init*](#cb_init)

Callback invoked for each file/directory entry during traversal. If **NULL**, uses the default **cb\_find\_init**() callback.

[*cb\_fini*](#cb_fini)

Callback invoked after processing a directory's contents. If **NULL**, uses the default **cb\_common\_fini**() callback.

## [Traversal Algorithm](#Traversal_Algorithm)

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 &gt; 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:**

[*fp\_max\_depth*](#fp_max_depth)

Maximum directory depth to traverse (0 = no limit).

[*fp\_min\_depth*](#fp_min_depth)

Minimum directory depth before processing entries.

[*fp\_recursive*](#fp_recursive)

Enable recursive directory descent (default: enabled).

[*fp\_thread\_count*](#fp_thread_count)

Number of parallel worker threads:

- 0 or 1: Single-threaded depth-first traversal
- &gt;1: Parallel traversal with work-stealing queue

[*fp\_stop\_on\_error*](#fp_stop_on_error)

If set, stop entire traversal on first error. In parallel mode, sets *fwq\_shutdown* flag to terminate all workers.

## [Internal Traversal Implementation](#Internal_Traversal_Implementation)

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)](/man/enterprise-packages/lustre/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)](/man/enterprise-packages/lustre/fdopendir.3)

5\.

Iterates through directory entries using [**readdir**(3)](/man/enterprise-packages/lustre/readdir.3)

**For each directory entry:**

- Skips "." and ".." entries
- Appends entry name to path buffer (with '/' separator)
- For **DT\_DIR** (directories):

<!--THE END-->

- In single-threaded mode: recursively calls **llapi\_semantic\_traverse**()
- In parallel mode: calls **work\_unit\_create\_and\_add**() to enqueue for worker threads

<!--THE END-->

- 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* &lt; *fp\_min\_depth*, callbacks are skipped (but traversal continues).

**File descriptor optimization:** Uses [**openat**(2)](/man/enterprise-packages/lustre/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.

## [Work Queue Architecture (Parallel Mode)](#Work_Queue_Architecture_%28Parallel_Mode%29)

When *fp\_thread\_count* &gt; 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:**

[**Enqueue**](#Enqueue)

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

[**Dequeue**](#Dequeue)

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

[**Work Stealing**](#Work)

Idle workers automatically "steal" work from the shared queue. This provides dynamic load balancing without requiring work distribution logic.

[**Completion Detection**](#Completion)

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.

[**Error Handling**](#Error)

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

## [Callback Interface](#Callback_Interface)

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:

[*path*](#path)

Full path to the current file/directory being processed.

[*p*](#p)

File descriptor of the parent directory (-1 if not available).

[*d*](#d)

Pointer to file descriptor of current entry (NULL for non-directories).

[*param*](#param)

Pointer to the **find\_param** structure. The *fp\_cb\_data* field can be used to pass custom callback data.

[*de*](#de)

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.

[**Other**](#Other)

Error code (negative values).

## [Matching Conditions](#Matching_Conditions)

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:**

[*fp\_type*](#fp_type)

File type to match (S\_IFREG, S\_IFDIR, S\_IFLNK, etc.). Set *fp\_exclude\_type* to invert the match.

[*fp\_pattern*](#fp_pattern)

Filename pattern using [**fnmatch**(3)](/man/enterprise-packages/lustre/fnmatch.3) syntax. Set *fp\_exclude\_pattern* to exclude matching names.

**Owner and Permissions:**

[*fp\_uid*](#fp_uid)

User ID to match. Set *fp\_check\_uid* to enable and *fp\_exclude\_uid* to invert.

[*fp\_gid*](#fp_gid)

Group ID to match. Set *fp\_check\_gid* to enable and *fp\_exclude\_gid* to invert.

[*fp\_perm*](#fp_perm)

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.

[*fp\_projid*](#fp_projid)

Project ID to match. Set *fp\_check\_projid* to enable and *fp\_exclude\_projid* to invert.

**Time-based Matching:**

[*fp\_atime*](#fp_atime)

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.

[*fp\_mtime*](#fp_mtime)

Modification time. Same semantics as *fp\_atime*. Use *fp\_msign* and *fp\_exclude\_mtime*.

[*fp\_ctime*](#fp_ctime)

Change time. Same semantics as *fp\_atime*. Use *fp\_csign* and *fp\_exclude\_ctime*.

[*fp\_btime*](#fp_btime)

Birth/creation time. Same semantics as *fp\_atime*. Use *fp\_bsign* and *fp\_exclude\_btime*.

[*fp\_newerxy*](#fp_newerxy)

Enable **--newerXY** comparison. Uses *fp\_newery* array for reference times indexed by **NEWERXY\_ATIME** (0), [**NEWERXY\_MTIME**(1)](/man/enterprise-packages/lustre/NEWERXY_MTIME.1), [**NEWERXY\_CTIME**(2)](/man/enterprise-packages/lustre/NEWERXY_CTIME.2), [**NEWERXY\_BTIME**(3)](/man/enterprise-packages/lustre/NEWERXY_BTIME.3).

**Size and Blocks:**

[*fp\_size*](#fp_size)

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.

[*fp\_blocks*](#fp_blocks)

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:**

[*fp\_stripe\_size*](#fp_stripe_size)

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.

[*fp\_stripe\_count*](#fp_stripe_count)

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.

[*fp\_poolname*](#fp_poolname)

OST pool name. Set *fp\_check\_pool* to enable and *fp\_exclude\_pool* to invert.

[*fp\_layout*](#fp_layout)

Layout pattern (RAID0, MDT, etc.). Set *fp\_check\_layout* to enable and *fp\_exclude\_layout* to invert.

[*fp\_comp\_count*](#fp_comp_count)

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.

[*fp\_comp\_flags*](#fp_comp_flags)

Component flags to match. Set *fp\_check\_comp\_flags* to enable. Use *fp\_comp\_neg\_flags* for flags that must not be set.

[*fp\_comp\_start*](#fp_comp_start)

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.

[*fp\_comp\_end*](#fp_comp_end)

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.

[*fp\_comp\_id*](#fp_comp_id)

Component ID to match. Set *fp\_check\_comp\_id* to enable and *fp\_exclude\_comp\_id* to invert.

[*fp\_mirror\_count*](#fp_mirror_count)

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.

[*fp\_mirror\_state*](#fp_mirror_state)

Mirror state to match. Set *fp\_check\_mirror\_state* to enable. Use *fp\_mirror\_neg\_state* for states that must not be set.

[*fp\_mirror\_id*](#fp_mirror_id)

Mirror ID to match. Set *fp\_check\_mirror\_id* to enable and *fp\_exclude\_mirror\_id* to invert.

[*fp\_mirror\_index*](#fp_mirror_index)

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.

[*fp\_ext\_size*](#fp_ext_size)

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:**

[*fp\_obd\_uuid*](#fp_obd_uuid)

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.

[*fp\_mdt\_uuid*](#fp_mdt_uuid)

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.

[*fp\_mdt\_count*](#fp_mdt_count)

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.

[*fp\_hash\_type*](#fp_hash_type)

Hash type for striped directories.

[*fp\_hash\_inflags*](#fp_hash_inflags)

Hash flags that must be set. Set *fp\_check\_hash\_flag* to enable.

[*fp\_hash\_exflags*](#fp_hash_exflags)

Hash flags that must not be set. Set *fp\_exclude\_hash\_type* to enable.

**Extended Attributes:**

[*fp\_xattr\_match\_info*](#fp_xattr_match_info)

Extended attribute matching using regular expressions. Configure with **xattr\_match\_info** structure for name/value pattern matching.

**File Attributes:**

[*fp\_attrs*](#fp_attrs)

File attributes that must be set (statx attributes). Set *fp\_exclude\_attrs* to invert.

[*fp\_neg\_attrs*](#fp_neg_attrs)

File attributes that must not be set.

[*fp\_nlink*](#fp_nlink)

Number of hard links. Set *fp\_nlink\_sign* to -1/0/1. Set *fp\_exclude\_nlink* to invert.

**Foreign Files:**

[*fp\_foreign\_type*](#fp_foreign_type)

Foreign file/directory type. Set *fp\_check\_foreign* to match foreign files and *fp\_exclude\_foreign* to exclude them.

**Compression (if supported):**

[*fp\_compr\_type*](#fp_compr_type)

Compression type. Set *fp\_check\_compr\_type* to enable and *fp\_exclude\_compr\_type* to invert.

[*fp\_compr\_lvl*](#fp_compr_lvl)

Compression level. Set *fp\_check\_compr\_lvl* to enable, *fp\_compr\_lvl\_sign* to -1/0/1. Set *fp\_exclude\_compr\_lvl* to invert.

[*fp\_compr\_chunk*](#fp_compr_chunk)

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:**

[*fp\_verbose*](#fp_verbose)

Verbosity level using **llapi\_layout\_verbose** enum values (VERBOSE\_DEFAULT, VERBOSE\_DETAIL, etc.).

[*fp\_quiet*](#fp_quiet)

Suppress output.

[*fp\_format\_printf\_str*](#fp_format_printf_str)

Custom printf-style format string for output (similar to **lfs find --printf**).

[*fp\_yaml*](#fp_yaml)

Output layout information in YAML format.

**Performance and Behavior:**

[*fp\_lazy*](#fp_lazy)

Use lazy size/block attributes when available.

[*fp\_stop\_on\_error*](#fp_stop_on_error~2)

Stop traversal on first error instead of continuing.

[*fp\_skip\_percent*](#fp_skip_percent)

Percentage of entries to skip (for sampling).

# [RETURN VALUES](#RETURN_VALUES)

**0**

on success.

**&lt;0**

negative error code on failure and sets **errno** appropriately.

# [ERRORS](#ERRORS)

[**EINVAL**](#EINVAL)

Invalid arguments (e.g., path too long, invalid parameters).

[**ENOMEM**](#ENOMEM)

Insufficient memory for traversal or thread pool.

[**ENOENT**](#ENOENT)

Starting path does not exist.

[**EACCES**](#EACCES)

Permission denied accessing files or directories.

[**ENOTDIR**](#ENOTDIR)

Path component is not a directory.

[**EOPNOTSUPP**](#EOPNOTSUPP)

Operation not supported (e.g., btime on filesystems without support).

# [EXAMPLES](#EXAMPLES)

## [Basic File Search](#Basic_File_Search)

```
#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;
}
```

## [Search with Lustre-specific Criteria](#Search_with_Lustre-specific_Criteria)

```
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);
```

## [Parallel Traversal with Custom Callback](#Parallel_Traversal_with_Custom_Callback)

```
#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;
}
```

# [NOTES](#NOTES)

•

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* &gt; 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.

# [AVAILABILITY](#AVAILABILITY)

**llapi\_find()** is part of the [**lustre**(7)](/man/enterprise-packages/lustre/lustre.7) user application interface library.

**llapi\_find\_with\_cb()** was added to support custom callbacks and parallel find operations.

# [SEE ALSO](#SEE_ALSO)

[**lfs-find**(1)](/man/enterprise-packages/lustre/lfs-find.1), [**llapi\_getstripe**(3)](/man/enterprise-packages/lustre/llapi_getstripe.3), [**llapi\_layout**(7)](/man/enterprise-packages/lustre/llapi_layout.7), [**lustreapi**(7)](/man/enterprise-packages/lustre/lustreapi.7), [**fnmatch**(3)](/man/enterprise-packages/lustre/fnmatch.3), [**stat**(2)](/man/enterprise-packages/lustre/stat.2), [**statx**(2)](/man/enterprise-packages/lustre/statx.2), [**open**(2)](/man/enterprise-packages/lustre/open.2), [**openat**(2)](/man/enterprise-packages/lustre/openat.2), [**fdopendir**(3)](/man/enterprise-packages/lustre/fdopendir.3), [**readdir**(3)](/man/enterprise-packages/lustre/readdir.3), [**pthread\_create**(3)](/man/enterprise-packages/lustre/pthread_create.3), [**pthread\_mutex\_lock**(3)](/man/enterprise-packages/lustre/pthread_mutex_lock.3), [**pthread\_cond\_wait**(3)](/man/enterprise-packages/lustre/pthread_cond_wait.3)

2026-07-03 Lustre User API

---

Document: llapi_find(3) from lustre 2.17.60-r0 (Chainguard OS)
Source: https://github.com/lustre/lustre-release
