4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 1989 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984 AT&T */
28 /* All Rights Reserved */
30 #pragma ident "%Z%%M% %I% %E% SMI" /* from S5R2 1.2 */
33 /***************************************************************
34 * ftw - file tree walk
36 * int ftw (path, fn, depth) char *path; int (*fn)(); int depth;
38 * Given a path name, ftw starts from the file given by that path
39 * name and visits each file and directory in the tree beneath
40 * that file. If a single file has multiple links within the
41 * structure, it will be visited once for each such link.
42 * For each object visited, fn is called with three arguments.
43 * The first contains the path name of the object, the second
44 * contains a pointer to a stat buffer which will usually hold
45 * appropriate information for the object and the third will
46 * contain an integer value giving additional information about
48 * FTW_F The object is a file for which stat was
49 * successful. It does not guarantee that the
50 * file can actually be read.
52 * FTW_D The object is a directory for which stat and
53 * open for read were both successful.
55 * FTW_DNR The object is a directory for which stat
56 * succeeded, but which cannot be read. Because
57 * the directory cannot be read, fn will not be
58 * called for any descendants of this directory.
60 * FTW_NS Stat failed on the object because of lack of
61 * appropriate permission, or because the object is a
62 * symbolic link that points to a non-existent file.
63 * This indication will be given, for example, for each
64 * file in a directory with read but no execute
65 * permission. Because stat failed, it is not
66 * possible to determine whether this object is a file
67 * or a directory. The stat buffer passed to fn will
68 * contain garbage. Stat failure for any reason
69 * other than lack of permission will be
70 * considered an error and will cause ftw to stop
71 * and return -1 to its caller.
73 * If fn returns nonzero, ftw stops and returns the same value
74 * to its caller. If ftw gets into other trouble along the way,
75 * it returns -1 and leaves an indication of the cause in errno.
77 * The third argument to ftw does not limit the depth to which
78 * ftw will go. Rather, it limits the depth to which ftw will
79 * go before it starts recycling file descriptors. In general,
80 * it is necessary to use a file descriptor for each level of the
81 * tree, but they can be recycled for deep trees by saving the
82 * position, closing, re-opening, and seeking. It is possible
83 * to start recycling file descriptors by sensing when we have
84 * run out, but in general this will not be terribly useful if
85 * fn expects to be able to open files. We could also figure out
86 * how many file descriptors are available and guarantee a certain
87 * number to fn, but we would not know how many to guarantee,
88 * and we do not want to impose the extra overhead on a caller who
89 * knows how many are available without having to figure it out.
91 * It is possible for ftw to die with a memory fault in the event
92 * of a file system so deeply nested that the stack overflows.
93 **************************************************************/
95 #include <sys/types.h>
103 extern char *malloc(), *strcpy();
115 char *subpath
, *component
;
119 /* Try to get file status.
120 If unsuccessful, errno will say why. */
121 if(stat(path
, &sb
) < 0) {
122 if (errno
== EACCES
) {
123 return((*fn
)(path
, &sb
, FTW_NS
));
124 } else if (errno
== ENOENT
) {
125 /* Check if symbolic link points to non-existent file */
126 if (lstat(path
, &sb
) < 0) {
129 else if ((sb
.st_mode
& S_IFMT
) == S_IFLNK
) {
131 return((*fn
)(path
, &sb
, FTW_NS
));
142 * The stat succeeded, so we know the object exists.
143 * If not a directory, call the user function and return.
145 if((sb
.st_mode
& S_IFMT
) != S_IFDIR
)
146 return((*fn
)(path
, &sb
, FTW_F
));
149 * The object was a directory.
151 * Open a file to read the directory
153 dirp
= opendir(path
);
156 * Call the user function, telling it whether
157 * the directory can be read. If it can't be read
158 * call the user function or indicate an error,
159 * depending on the reason it couldn't be read.
162 return(errno
== EACCES
? (*fn
)(path
, &sb
, FTW_DNR
): -1);
164 /* We could read the directory. Call user function. */
165 rc
= (*fn
)(path
, &sb
, FTW_D
);
169 /* Allocate a buffer to hold generated pathnames. */
171 subpath
= malloc((unsigned)(n
+MAXNAMLEN
+2));
172 if(subpath
== NULL
) {
178 /* Create a prefix to which we will append component names */
179 (void)strcpy(subpath
, path
);
180 if(subpath
[0] != '\0' && subpath
[n
-1] != '/')
182 component
= &subpath
[n
];
185 * Read the directory one component at a time.
186 * We must ignore "." and "..", but other than that,
187 * just create a path name and call self to check it out.
189 while((dp
= readdir(dirp
)) != NULL
) {
190 if(strcmp(dp
->d_name
, ".") != 0 &&
191 strcmp(dp
->d_name
, "..") != 0) {
194 /* Append component name to the working path */
195 (void)strcpy(component
, dp
->d_name
);
198 * If we are about to exceed our depth,
199 * remember where we are and close a file.
202 here
= telldir(dirp
);
207 * Do a recursive call to process the file.
208 * (watch this, sports fans)
210 rc
= ftw(subpath
, fn
, depth
-1);
219 * If we closed the file, try to reopen it.
222 dirp
= opendir(path
);
233 * We got out of the subdirectory loop. The return from
234 * the final readdir is in dp. Clean up.