ftw(3)
β¨ ftw, nftw β file tree walk β¨
okay bestie, sit down, hydrate, because we're about to walk an entire directory tree together and it's gonna be so cute π this is a manual page (ftw(3)) from the Library Functions Manual, and it lives in the Standard C library (libc, linked with -lc). two functions, one vibe: ftw() and nftw(). let's get into it.
πΈ SYNOPSIS πΈ
#include <ftw.h>
int nftw(const char *dirpath,
typeof(int (const char *fpath, const struct stat *sb,
int typeflag, struct FTW *ftwbuf))
*fn,
int nopenfd, int flags);
[[deprecated]]
int ftw(const char *dirpath,
typeof(int (const char *fpath, const struct stat *sb,
int typeflag))
*fn,
int nopenfd);
π Feature Test Macro Requirements for glibc (peep feature_test_macros(7)) π
nftw():
_XOPEN_SOURCE >= 500
like, if you want nftw() you gotta define that macro first, no cap. π
π DESCRIPTION π
nftw() walks through the entire directory tree sitting under the directory dirpath, and it calls fn() once for every single entry in that tree. by default, directories get handled before the files and subdirectories they contain β that's a preorder traversal, and honestly? iconic behaviour.
now, to avoid absolutely devouring all of the calling process's file descriptors (we been knew, fd's are precious), nopenfd tells you the maximum number of directories that nftw() will hold open at the same time. when the search depth goes past that number, nftw() gets slower because directories have to be closed and then reopened. nftw() uses at most one file descriptor for each level in the directory tree. so like, it's not greedy. it's mindful. main character energy.
for each entry found in the tree, nftw() calls fn() with four arguments: fpath, sb, typeflag, and ftwbuf.
fpathis the pathname of the entry. it's expressed either as a pathname relative to the calling process's current working directory at the time of the call tonftw()(ifdirpathwas given as a relative pathname), or as an absolute pathname (ifdirpathwas given as an absolute pathname).sbis a pointer to thestatstructure returned by a call tostat(2)forfpath. π«Ά
the typeflag argument passed to fn() is an integer with one of these values:
FTW_Fβfpathis a regular file. classic.FTW_Dβfpathis a directory. periodt.FTW_DNRβfpathis a directory that can't be read. πFTW_DPβfpathis a directory, andFTW_DEPTHwas specified inflags. (heads up: ifFTW_DEPTHwas not specified inflags, then directories will always be visited withtypeflagset toFTW_D.) all of the files and subdirectories withinfpathhave been processed. so it's the after-party version ofFTW_D.FTW_NSβ thestat(2)call failed onfpath, which is not a symbolic link. the probable cause? the caller had read permission on the parent directory, so the filenamefpathcould be seen, but did not have execute permission, so the file couldn't be reached forstat(2). in this case, the contents of the buffer pointed to bysbare undefined. π do not read that buffer, bestie, it's giving chaos.FTW_SLβfpathis a symbolic link, andFTW_PHYSwas set inflags.FTW_SLNβfpathis a symbolic link pointing to a nonexistent file. (this only happens ifFTW_PHYSis not set.) when it does, thesbargument passed tofn()contains the information returned by performinglstat(2)on the "dangling" symbolic link. (but like, seeBUGSbelow. π)
the fourth argument (ftwbuf) that nftw() hands over when calling fn() is a pointer to a structure of type FTW:
struct FTW {
int base;
int level;
};
baseis the offset of the filename (i.e., the basename component) in the pathname given infpath.levelis the depth offpathin the directory tree, relative to the root of the tree (dirpath, which has depth 0).
to stop the tree walk, fn() returns a nonzero value, and that value becomes the return value of nftw(). as long as fn() returns 0, nftw() keeps going β either until it's traversed the entire tree, in which case it returns zero, or until it hits an error (like a malloc(3) failure), in which case it returns -1.
β οΈ real talk: becausenftw()uses dynamic data structures, the only safe way to exit out of a tree walk is to return a nonzero value fromfn(). if you want a signal to be able to terminate the walk without causing a memory leak, have the handler set a global flag thatfn()checks. and do not uselongjmp(3)unless the program is going to terminate. i'm serious. π₯
π the flags argument π
flags is formed by ORing together zero or more of these babes:
FTW_ACTIONRETVAL(since glibc 2.3.3) β if this glibc-specific flag is set, thennftw()handles the return value fromfn()differently.fn()should then return one of these values:FTW_CONTINUEβ tellsnftw()to continue normally. slay.FTW_SKIP_SIBLINGSβ iffn()returns this, then siblings of the current entry get skipped, and processing continues in the parent.FTW_SKIP_SUBTREEβ iffn()is called with an entry that's a directory (typeflagisFTW_D), this return value prevents objects within that directory from being passed as arguments tofn().nftw()continues processing with the next sibling of the directory.FTW_STOPβ causesnftw()to return immediately, with the return valueFTW_STOP.
other return values could get associated with new actions in the future, so fn() should not return values other than the ones listed above. and heads up: you need to define the feature test macro _GNU_SOURCE (before including any header files) in order to get the definition of FTW_ACTIONRETVAL from <ftw.h>. π
FTW_CHDIRβ if set, do achdir(2)into each directory before handling its contents. this is useful if the program needs to do some action in the directory wherefpathresides. (specifying this flag has no effect on the pathname that gets passed in thefpathargument offn().)FTW_DEPTHβ if set, do a post-order traversal, that is, callfn()for the directory itself after handling the contents of the directory and its subdirectories. (by default, each directory is handled before its contents β so this flips the whole thing. π)FTW_MOUNTβ if set, stay within the same filesystem (i.e., do not cross mount points).FTW_PHYSβ if set, do not follow symbolic links. (this is what you want.) if not set, symbolic links are followed, but no file gets reported twice. and ifFTW_PHYSis not set butFTW_DEPTHis set, then the functionfn()is never called for a directory that would be a descendant of itself. π
π¦ ftw() π¦
ftw() is the older function, and it offers a subset of what nftw() can do. the notable differences are:
ftw()has noflagsargument. it behaves the same as whennftw()is called withflagsspecified as zero.- the callback function,
fn(), is not supplied with a fourth argument. - the range of values passed via the
typeflagargument tofn()is smaller: justFTW_F,FTW_D,FTW_DNR,FTW_NS, and (possibly)FTW_SL.
π RETURN VALUE π
these functions return 0 on success, and -1 if an error occurs.
if fn() returns nonzero, then the tree walk is terminated, and the value returned by fn() is returned as the result of ftw() or nftw().
if nftw() is called with the FTW_ACTIONRETVAL flag, then the only nonzero value that should be used by fn() to terminate the tree walk is FTW_STOP, and that value is returned as the result of nftw().
β ATTRIBUTES β
for an explanation of the terms used in this section, peep attributes(7). (the original is a lil table, but we're doing it as a list because tables are not the vibe here π
)
nftw()β Thread safety β MT-Safe cwdftw()β Thread safety β MT-Safe
πΈ VERSIONS πΈ
in some implementations (e.g., glibc), ftw() will never use FTW_SL. on other systems, FTW_SL occurs only for symbolic links that do not point to an existing file. and again, on other systems, ftw() will use FTW_SL for each symbolic link. π΅βπ«
if fpath is a symbolic link and stat(2) failed, POSIX.1-2008 states that it is undefined whether FTW_NS or FTW_SL gets passed in typeflag. so like⦠for predictable results, use nftw(). fr fr.
π STANDARDS π
POSIX.1-2008.
π HISTORY π
ftw()β POSIX.1-2001, SVr4, SUSv1. POSIX.1-2008 marks it as obsolete. rip boo, you had a good run. π«‘nftw()β glibc 2.1. POSIX.1-2001, SUSv1.FTW_SLβ POSIX.1-2001, SUSv1.
π NOTES π
POSIX.1-2008 notes that the results are unspecified if fn does not preserve the current working directory. so like, don't be out here wrecking the cwd, bestie. π
π BUGS π
according to POSIX.1-2008, when the typeflag argument passed to fn() contains FTW_SLN, the buffer pointed to by sb should contain information about the dangling symbolic link (obtained by calling lstat(2) on the link).
early glibc versions correctly followed the POSIX specification on this point. however β as a result of a regression introduced in glibc 2.4, the contents of the buffer pointed to by sb were undefined when FTW_SLN is passed in typeflag. (more precisely, the contents of the buffer were left unchanged in this case.) π this regression was eventually fixed in glibc 2.30, so that the glibc implementation (once more) follows the POSIX specification. we love a redemption arc. β¨
β¨ EXAMPLES β¨
okay bestie, demo time π this lil program walks the whole directory tree under whatever path you hand it as the first command-line argument β and if you're feeling lazy and don't pass one at all? it just chills in the current directory instead π«Ά and while it's strolling around, it prints out all kinds of cute little facts about every file it bumps into.
oh and there's a second command-line argument too, bestie β that one's a string of characters that control what value gets assigned to the flags argument when nftw() gets called. character-driven flag selection, it's giving β¨configurableβ¨
π Program source π
okay here's the whole thing, copy it, compile it, slay it:
#define _XOPEN_SOURCE 500
#include <ftw.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int
display_info(const char *fpath, const struct stat *sb,
int tflag, struct FTW *ftwbuf)
{
printf("%-3s %2d ",
(tflag == FTW_D) ? "d" : (tflag == FTW_DNR) ? "dnr" :
(tflag == FTW_DP) ? "dp" : (tflag == FTW_F) ? "f" :
(tflag == FTW_NS) ? "ns" : (tflag == FTW_SL) ? "sl" :
(tflag == FTW_SLN) ? "sln" : "???",
ftwbuf->level);
if (tflag == FTW_NS)
printf("-------");
else
printf("%7jd", (intmax_t) sb->st_size);
printf(" %-40s %d %s\n",
fpath, ftwbuf->base, fpath + ftwbuf->base);
return 0; /* To tell nftw() to continue */
}
int
main(int argc, char *argv[])
{
int flags = 0;
if (argc > 2 && strchr(argv[2], 'd') != NULL)
flags |= FTW_DEPTH;
if (argc > 2 && strchr(argv[2], 'p') != NULL)
flags |= FTW_PHYS;
if (nftw((argc < 2) ? "." : argv[1], display_info, 20, flags)
== -1)
{
perror("nftw");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
no cap, the callback returning 0 is literally the "keep going bestie" signal β that's the whole /* To tell nftw() to continue */ moment π₯Ή iconic.
π SEE ALSO π
stat(2)β the file-stat syscall lore πfts(3)β the other tree-walker in the group chat πreaddir(3)β for when you just wanna read one directory at a time π
and that's the whole page bestie, we did it π this was straight from Linux man-pages 6.20, dated 2026-02-08 β go forth, walk your trees, and remember: return 0 to keep going, and always free your π₯main character energyπ₯. periodt π
β¨π¦
π¦ where this page comes from
- arch rolling: man-pages 6.20-1 Β· BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- π the page's license: GPL-2.0-or-later
- Copyright 1999, Andries E. Brouwer <aeb@cwi.nl>
- Copyright 2006, Justin Pryzby <justinpryzby@users.sf.net>
- Copyright 2006, Michael Kerrisk <mtk.manpages@gmail.com>
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms