4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1988 AT&T */
28 /* All Rights Reserved */
30 #pragma ident "%Z%%M% %I% %E% SMI"
33 * nftw - new file tree walk
35 * int nftw(char *path, int (*fn)(), int depth, int flags);
37 * Derived from System V ftw() by David Korn
39 * nftw visits each file and directory in the tree starting at
40 * path. It uses the generic directory reading library so it works
41 * for any file system type. The flags field is used to specify:
42 * FTW_PHYS Physical walk, does not follow symbolic links
43 * Otherwise, nftw will follow links but will not
44 * walk down any path the crosses itself.
45 * FTW_MOUNT The walk will not cross a mount point.
46 * FTW_DEPTH All subdirectories will be visited before the
48 * FTW_CHDIR The walk will change to each directory before
49 * reading it. This is faster but core dumps
50 * may not get generated.
52 * The following flags are private, and are used by the find
54 * FTW_ANYERR Call the callback function and return
55 * FTW_NS on any stat failure, not just
57 * FTW_HOPTION Use stat the first time the walk
58 * function is called, regardless of
59 * whether or not FTW_PHYS is specified.
60 * FTW_NOLOOP Allow find utility to detect infinite loops created
61 * by both symbolic and hard linked directories.
63 * fn is called with four arguments at each file and directory.
64 * The first argument is the pathname of the object, the second
65 * is a pointer to the stat buffer and the third is an integer
66 * giving additional information as follows:
68 * FTW_F The object is a file.
69 * FTW_D The object is a directory.
70 * FTW_DP The object is a directory and subdirectories
72 * FTW_SL The object is a symbolic link.
73 * FTW_SLN The object is a symbolic link pointing at a
75 * FTW_DNR The object is a directory that cannot be read.
76 * fn will not be called for any of its descendants.
77 * FTW_NS Stat failed on the object because of lack of
78 * appropriate permission. The stat buffer passed to fn
79 * is undefined. Stat failure for any reason is
80 * considered an error and nftw will return -1.
81 * The following value is private, and is used by the find utility:
82 * FTW_DL An infinite loop has been detected.
83 * The fourth argument is a struct FTW* which contains the depth
84 * and the offset into pathname to the base name.
85 * If fn returns nonzero, nftw returns this value to its caller.
87 * depth limits the number of open directories that ftw uses
88 * before it starts recycling file descriptors. In general,
89 * a file descriptor is used for each level. When FTW_CHDIR isn't set,
90 * in order to descend to arbitrary depths, nftw requires 2 file
91 * descriptors to be open during the call to openat(), therefore if
92 * the depth argument is less than 2 nftw will not use openat(), and
93 * it will fail with ENAMETOOLONG if it descends to a directory that
100 #include <sys/types.h>
101 #include <sys/stat.h>
115 #if !defined(_LP64) && _FILE_OFFSET_BITS == 64
118 #define fstat fstat64
119 #define fstatat fstatat64
120 #pragma weak _nftw64 = nftw64
122 #pragma weak _nftw = nftw
123 #endif /* !_LP64 && _FILE_OFFSET_BITS == 64 */
126 #define PATH_MAX 1023
130 * Local variables (used to be static local).
131 * Putting them into a structure that is passed
132 * around makes nftw() MT-safe with no locking required.
152 int (*statf
)(const char *, struct stat
*, struct Save
*, int flags
);
153 int (*savedstatf
)(const char *, struct stat
*, struct Save
*,
155 DIR *(*opendirf
)(const char *);
158 static int oldclose(struct Save
*);
159 static int cdlstat(const char *, struct stat
*, struct Save
*, int flags
);
160 static int cdstat(const char *, struct stat
*, struct Save
*, int flags
);
161 static int nocdlstat(const char *, struct stat
*, struct Save
*, int flags
);
162 static int nocdstat(const char *, struct stat
*, struct Save
*, int flags
);
163 static DIR *cdopendir(const char *);
164 static DIR *nocdopendir(const char *);
165 static const char *get_unrooted(const char *);
168 * This is the recursive walker.
171 walk(char *component
,
172 int (*fn
)(const char *, const struct stat
*, int, struct FTW
*),
173 int depth
, struct Save
*last
, struct Var
*vp
)
187 size_t base_comp
, base_component
, base_this_comp
, base_last_comp
;
188 size_t base_fullpath
, base_tmppath
;
192 if ((vp
->curflags
& FTW_CHDIR
) && last
)
197 if (vp
->savedstatf
== NULL
)
198 vp
->savedstatf
= vp
->statf
;
200 if ((vp
->walklevel
++ == 0) && (vp
->curflags
& FTW_HOPTION
)) {
201 if (((vp
->curflags
& FTW_CHDIR
) == 0) && (depth
>= 2)) {
202 vp
->statf
= nocdstat
;
207 vp
->statf
= vp
->savedstatf
;
211 * Determine the type of the component.
213 * Note that if the component is a trigger mount, this
214 * will cause it to load.
216 if ((*vp
->statf
)(comp
, &statb
, last
, _AT_TRIGGER
) >= 0) {
217 if ((statb
.st_mode
& S_IFMT
) == S_IFDIR
) {
220 (void) oldclose(last
);
221 if ((this.fd
= (*vp
->opendirf
)(comp
)) == 0) {
222 if (errno
== EMFILE
&& oldclose(last
) &&
223 (this.fd
= (*vp
->opendirf
)(comp
)) != 0) {
225 * If opendirf fails because there
226 * are OPEN_MAX fd in the calling
227 * process, and we close the oldest
228 * fd, and another opendirf doesn't
229 * fail, depth is set to 1.
237 } else if ((statb
.st_mode
& S_IFMT
) == S_IFLNK
) {
242 } else if ((vp
->curflags
& FTW_ANYERR
) && errno
!= ENOENT
) {
244 * If FTW_ANYERR is specified, then a stat error
245 * other than ENOENT automatically results in
246 * failure. This allows the callback function
247 * to properly handle ENAMETOOLONG and ELOOP and
248 * things of that nature, that would be masked
249 * by calling lstat before failing.
255 * Statf has failed. If stat was used instead of lstat,
256 * try using lstat. If lstat doesn't fail, "comp"
257 * must be a symbolic link pointing to a non-existent
258 * file. Such a symbolic link should be ignored.
259 * Also check the file type, if possible, for symbolic
262 if (((vp
->statf
== cdstat
) &&
263 (cdlstat(comp
, &statb
, last
, 0) >= 0) &&
264 ((statb
.st_mode
& S_IFMT
) == S_IFLNK
)) ||
265 ((vp
->statf
== nocdstat
) &&
266 (nocdlstat(comp
, &statb
, last
, 0) >= 0) &&
267 ((statb
.st_mode
& S_IFMT
) == S_IFLNK
))) {
270 * Ignore bad symbolic link, let "fn"
280 * if FTW_ANYERR is set in flags, we call
281 * the user function with FTW_NS set, regardless
282 * of the reason stat failed.
284 if (!(vp
->curflags
& FTW_ANYERR
))
291 * If the walk is not supposed to cross a mount point,
292 * and it did, get ready to return.
294 if ((vp
->curflags
& FTW_MOUNT
) && type
!= FTW_NS
&&
295 statb
.st_dev
!= vp
->cur_mount
)
300 * If current component is not a directory, call user
301 * specified function and get ready to return.
303 if (type
!= FTW_D
|| (vp
->curflags
& FTW_DEPTH
) == 0)
304 rc
= (*fn
)(vp
->tmppath
, &statb
, type
, &vp
->state
);
307 skip
= (vp
->state
.quit
& FTW_SKD
);
308 if (rc
!= 0 || type
!= FTW_D
|| (vp
->state
.quit
& FTW_PRUNE
))
311 if (vp
->tmppath
[0] != '\0' && component
[-1] != '/')
314 if (vp
->curflags
& FTW_CHDIR
) {
318 * Security check (there is a window between
319 * (*vp->statf)() and opendir() above).
321 if ((vp
->curflags
& FTW_PHYS
) &&
322 (fstat(this.fd
->dd_fd
, &statb2
) < 0 ||
323 statb2
.st_ino
!= statb
.st_ino
||
324 statb2
.st_dev
!= statb
.st_dev
)) {
330 if ((cdval
= fchdir(this.fd
->dd_fd
)) >= 0) {
331 this.comp
= component
;
334 rc
= (*fn
)(vp
->tmppath
, &statb
, type
, &vp
->state
);
340 * If the walk has followed a symbolic link (FTW_PHYS is not set),
341 * traverse the walk back to make sure there is not a loop.
342 * The find utility (FTW_NOLOOP is set) detects infinite loops
343 * in both symbolic and hard linked directories.
345 if ((vp
->curflags
& FTW_NOLOOP
) ||
346 ((vp
->curflags
& FTW_PHYS
) == 0)) {
347 struct Save
*sp
= last
;
350 * If the same node has already been visited, there
351 * is a loop. Get ready to return.
353 if (sp
->dev
== statb
.st_dev
&&
354 sp
->inode
== statb
.st_ino
) {
355 if (vp
->curflags
& FTW_NOLOOP
) {
356 /* private interface for find util */
365 this.dev
= statb
.st_dev
;
366 this.inode
= statb
.st_ino
;
367 oldbase
= vp
->state
.base
;
368 vp
->state
.base
= (int)(component
- vp
->tmppath
);
369 while (dir
= readdir(this.fd
)) {
376 else if (q
[1] == '.' && q
[2] == 0)
379 if (last
!= NULL
&& last
->comp
!= NULL
) {
380 base_last_comp
= last
->comp
- vp
->home
;
382 base_comp
= comp
- vp
->home
;
383 base_component
= component
- vp
->home
;
384 if ((strlen(q
) + strlen(vp
->home
) + 1) > vp
->len
) {
386 * When the space needed for vp->home has
387 * exceeded the amount of space that has
388 * been allocated, realloc() more space
389 * and adjust pointers to point to the
390 * (possibly moved) new block for vp->home
392 base_this_comp
= this.comp
- vp
->home
;
393 base_fullpath
= vp
->fullpath
- vp
->home
;
394 base_tmppath
= vp
->tmppath
- vp
->home
;
396 tmp
= (char *)realloc(vp
->home
, vp
->len
);
402 comp
= vp
->home
+ base_comp
;
403 component
= vp
->home
+ base_component
;
404 this.comp
= vp
->home
+ base_this_comp
;
405 vp
->fullpath
= vp
->home
+ base_fullpath
;
406 vp
->tmppath
= vp
->home
+ base_tmppath
;
407 if (last
!= NULL
&& last
->comp
!= NULL
) {
408 last
->comp
= vp
->home
+ base_last_comp
;
417 /* Call walk() recursively. */
418 rc
= walk(p
, fn
, depth
-1, &this, vp
);
419 if (last
!= NULL
&& last
->comp
!= NULL
) {
420 last
->comp
= vp
->home
+ base_last_comp
;
422 comp
= vp
->home
+ base_comp
;
423 component
= vp
->home
+ base_component
;
427 if (vp
->curflags
& FTW_CHDIR
) {
428 this.fd
= opendir(".");
430 this.fd
= (*vp
->opendirf
)(comp
);
436 seekdir(this.fd
, this.here
);
439 if (errno
== ENOENT
) {
440 (void) fprintf(stderr
, "cannot open %s: %s\n",
441 vp
->tmppath
, strerror(errno
));
445 goto quit
; /* this seems extreme */
448 vp
->state
.base
= oldbase
;
451 if ((vp
->tmppath
[0] != '\0') && (vp
->curflags
& FTW_DEPTH
) && !skip
)
452 rc
= (*fn
)(vp
->tmppath
, &statb
, type
, &vp
->state
);
454 if (cdval
>= 0 && last
) {
455 /* try to change back to previous directory */
456 if (last
->fd
!= NULL
) {
457 if (fchdir(last
->fd
->dd_fd
) < 0) {
461 if ((cdval
= chdir("..")) >= 0) {
462 if ((*vp
->statf
)(".", &statb
, last
, 0) < 0 ||
463 statb
.st_ino
!= last
->inode
||
464 statb
.st_dev
!= last
->dev
)
469 if (chdir(vp
->fullpath
) < 0) {
473 if ((vp
->curflags
& FTW_PHYS
) &&
474 ((*vp
->statf
)(".", &statb
,
476 statb
.st_ino
!= last
->inode
||
477 statb
.st_dev
!= last
->dev
)) {
487 (void) closedir(this.fd
);
495 nftw(const char *path
,
496 int (*fn
)(const char *, const struct stat
*, int, struct FTW
*),
497 int depth
, int flags
)
505 const char *savepath
= path
;
509 var
.len
= 2*(PATH_MAX
+1);
510 var
.home
= (char *)malloc(var
.len
);
511 if (var
.home
== NULL
)
517 * If the walk is going to change directory before
518 * reading it, save current working directory.
520 if (flags
& FTW_CHDIR
) {
521 if (getcwd(var
.home
, PATH_MAX
+1) == 0) {
526 endhome
= dp
= var
.home
+ strlen(var
.home
);
531 var
.fullpath
= var
.home
;
542 var
.state
.base
= (int)(base
+ 1 - var
.tmppath
);
545 errno
= ENAMETOOLONG
;
548 var
.curflags
= flags
;
551 * If doing chdir()'s, set var.opendirf to cdopendir.
552 * If not doing chdir()'s and if nftw()'s depth arg >= 2,
553 * set var.opendirf to nocdopendir. In order to
554 * descend to arbitrary depths without doing chdir()'s, nftw()
555 * requires a depth arg >= 2 so that nocdopendir() can use openat()
556 * to traverse the directories. So when not doing
557 * chdir()'s if nftw()'s depth arg <= 1, set var.opendirf to
559 * If doing a physical walk (not following symbolic link), set
560 * var.statf to cdlstat() or nocdlstat(). Otherwise, set var.statf
561 * to cdstat() or nocdstat().
563 if (((flags
& FTW_CHDIR
) == 0) && (depth
>= 2)) {
564 var
.opendirf
= nocdopendir
;
565 if (flags
& FTW_PHYS
)
566 var
.statf
= nocdlstat
;
568 var
.statf
= nocdstat
;
570 var
.opendirf
= cdopendir
;
571 if (flags
& FTW_PHYS
)
578 * If walk is not going to cross a mount point,
579 * save the current mount point.
581 if (flags
& FTW_MOUNT
) {
582 if ((*var
.statf
)(savepath
, &statb
, NULL
, 0) >= 0)
583 var
.cur_mount
= statb
.st_dev
;
590 * Call walk() which does most of the work.
591 * walk() uses errno in a rather obtuse way
592 * so we shield any incoming errno.
596 var
.savedstatf
= NULL
;
597 rc
= walk(dp
, fn
, depth
, (struct Save
*)0, &var
);
602 if (flags
& FTW_CHDIR
)
603 (void) chdir(var
.home
);
609 * Get stat info on path when FTW_CHDIR is set.
613 cdstat(const char *path
, struct stat
*statp
, struct Save
*lp
, int flags
)
615 return (fstatat(AT_FDCWD
, path
, statp
, flags
));
619 * Get lstat info on path when FTW_CHDIR is set.
623 cdlstat(const char *path
, struct stat
*statp
, struct Save
*lp
, int flags
)
625 return (fstatat(AT_FDCWD
, path
, statp
,
626 flags
| AT_SYMLINK_NOFOLLOW
));
630 * Get stat info on path when FTW_CHDIR is not set.
633 nocdstat(const char *path
, struct stat
*statp
, struct Save
*lp
, int flags
)
636 const char *basepath
;
639 /* get basename of path */
640 basepath
= get_unrooted(path
);
649 return (fstatat(fd
, basepath
, statp
, flags
));
653 * Get lstat info on path when FTW_CHDIR is not set.
656 nocdlstat(const char *path
, struct stat
*statp
, struct Save
*lp
, int flags
)
659 const char *basepath
;
662 /* get basename of path */
663 basepath
= get_unrooted(path
);
672 return (fstatat(fd
, basepath
, statp
, flags
| AT_SYMLINK_NOFOLLOW
));
676 * Open path directory when FTW_CHDIR is set.
680 cdopendir(const char *path
)
682 return (opendir(path
));
686 * Open path directory when FTW_CHDIR is not set.
689 nocdopendir(const char *path
)
693 char *dirp
, *token
, *ptr
;
695 if (((fdd
= opendir(path
)) == NULL
) && (errno
== ENAMETOOLONG
)) {
696 if ((dirp
= strdup(path
)) == NULL
) {
697 errno
= ENAMETOOLONG
;
700 if ((token
= strtok_r(dirp
, "/", &ptr
)) != NULL
) {
701 if ((fd
= openat(AT_FDCWD
, dirp
, O_RDONLY
)) < 0) {
703 errno
= ENAMETOOLONG
;
706 while ((token
= strtok_r(NULL
, "/", &ptr
)) != NULL
) {
707 if ((cfd
= openat(fd
, token
, O_RDONLY
)) < 0) {
710 errno
= ENAMETOOLONG
;
717 return (fdopendir(fd
));
720 errno
= ENAMETOOLONG
;
726 * return pointer basename of path, which may contain trailing slashes
728 * We do this when we do not chdir() on the input.
731 get_unrooted(const char *path
)
738 ptr
= path
+ strlen(path
);
739 /* find last char in path before any trailing slashes */
740 while (ptr
!= path
&& *--ptr
== '/')
743 if (ptr
== path
) /* all slashes */
754 * close the oldest directory. It saves the seek offset.
755 * return value is 0 unless it was unable to close any descriptor
759 oldclose(struct Save
*sp
)
764 if (spnext
== 0 || spnext
->fd
== 0)
768 if (sp
== 0 || sp
->fd
== 0)
770 sp
->here
= telldir(sp
->fd
);
771 (void) closedir(sp
->fd
);