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]
22 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1988 AT&T */
27 /* All Rights Reserved */
31 * ftw - file tree walk
33 * int ftw (path, fn, depth) char *path; int (*fn)(); int depth;
35 * Given a path name, ftw starts from the file given by that path
36 * name and visits each file and directory in the tree beneath
37 * that file. If a single file has multiple links within the
38 * structure, it will be visited once for each such link.
39 * For each object visited, fn is called with three arguments.
40 * The first contains the path name of the object, the second
41 * contains a pointer to a stat buffer which will usually hold
42 * appropriate information for the object and the third will
43 * contain an integer value giving additional information:
45 * FTW_F The object is a file for which stat was
46 * successful. It does not guarantee that the
47 * file can actually be read.
49 * FTW_D The object is a directory for which stat and
50 * open for read were both successful.
52 * FTW_DNR The object is a directory for which stat
53 * succeeded, but which cannot be read. Because
54 * the directory cannot be read, fn will not be
55 * called for any descendants of this directory.
57 * FTW_NS Stat failed on the object because of lack of
58 * appropriate permission. This indication will
59 * be given, for example, for each file in a
60 * directory with read but no execute permission.
61 * Because stat failed, it is not possible to
62 * determine whether this object is a file or a
63 * directory. The stat buffer passed to fn will
64 * contain garbage. Stat failure for any reason
65 * other than lack of permission will be
66 * considered an error and will cause ftw to stop
67 * and return -1 to its caller.
69 * If fn returns nonzero, ftw stops and returns the same value
70 * to its caller. If ftw gets into other trouble along the way,
71 * it returns -1 and leaves an indication of the cause in errno.
73 * The third argument to ftw does not limit the depth to which
74 * ftw will go. Rather, it limits the depth to which ftw will
75 * go before it starts recycling file descriptors. In general,
76 * it is necessary to use a file descriptor for each level of the
77 * tree, but they can be recycled for deep trees by saving the
78 * position, closing, re-opening, and seeking. It is possible
79 * to start recycling file descriptors by sensing when we have
80 * run out, but in general this will not be terribly useful if
81 * fn expects to be able to open files. We could also figure out
82 * how many file descriptors are available and guarantee a certain
83 * number to fn, but we would not know how many to guarantee,
84 * and we do not want to impose the extra overhead on a caller who
85 * knows how many are available without having to figure it out.
87 * It is possible for ftw to die with a memory fault in the event
88 * of a file system so deeply nested that the stack overflows.
91 #include <sys/fs/ufs_inode.h>
92 #include <sys/types.h>
106 int (*)(const char *, const struct stat64
*, int),
108 int (*)(const char *, struct stat64
*));
113 int (*fn
)(const char *, const struct stat64
*, int),
118 if ((pwdfd
= open(".", O_RDONLY
)) < 0) {
121 rc
= (lf_xftw(path
, fn
, depth
, lstat64
));
131 int (*fn
)(const char *, const struct stat64
*, int),
133 int (*statfn
)(const char *, struct stat64
*))
135 lf_xftw(char *path
, int (*fn
)(), int depth
, int (*statfn
)())
139 int rc
, sublen
, saverr
, attrfd
;
141 char *subpath
, *component
;
144 extern dev_t partial_dev
;
147 * Try to get file status.
148 * If unsuccessful, errno will say why.
150 if ((*statfn
)(path
, &sb
) < 0)
151 return (errno
== EACCES
? (*fn
)(path
, &sb
, FTW_NS
): -1);
153 * The stat succeeded, so we know the object exists.
154 * Make sure it is not a mount point for another filesystem.
155 * The following check must be made here because:
157 * + namefs can be mounted on anything, but a directory
158 * + all other filesystems must be mounted on a directory
160 if (sb
.st_dev
!= partial_dev
) {
164 * Check for presence of attributes on file
166 if (pathconf(path
, _PC_XATTR_EXISTS
) == 1) {
167 attrfd
= attropen64(path
, ".", O_RDONLY
|O_NONBLOCK
);
172 * If not a directory, call the user function and return.
174 if ((sb
.st_mode
& S_IFMT
) != S_IFDIR
&&
175 (sb
.st_mode
& IFMT
) != IFATTRDIR
) {
176 rc
= (*fn
)(path
, &sb
, FTW_F
);
177 if (rc
== 0 && attrfd
!= -1) {
178 (void) fchdir(attrfd
);
179 rc
= lf_xftw(".", fn
, depth
-1, statfn
);
180 (void) fchdir(pwdfd
);
181 (void) close(attrfd
);
186 * The object was a directory and not a mount point.
188 * Open a file to read the directory
190 dirp
= opendir(path
);
193 * Call the user function, telling it whether
194 * the directory can be read. If it can't be read
195 * call the user function or indicate an error,
196 * depending on the reason it couldn't be read.
199 rc
= (errno
== EACCES
? (*fn
)(path
, &sb
, FTW_DNR
): -1);
201 rc
= (*fn
)(path
, &sb
, FTW_D
);
203 * If the directory has attributes, process the
204 * attributes before processing the directory contents.
206 if (rc
== 0 && attrfd
!= -1) {
207 (void) fchdir(attrfd
);
208 rc
= lf_xftw(".", fn
, depth
-1, statfn
);
209 (void) fchdir(pwdfd
);
210 (void) close(attrfd
);
212 if (rc
!= 0 || dirp
== NULL
)
215 /* Allocate a buffer to hold generated pathnames. */
216 /* LINTED: the length will fit into a signed integer */
217 n
= (int)strlen(path
);
218 sublen
= n
+ MAXNAMLEN
+ 1; /* +1 for appended / */
219 subpath
= malloc((unsigned)(sublen
+1)); /* +1 for NUL */
220 if (subpath
== NULL
) {
222 (void) closedir(dirp
);
227 /* Create a prefix to which we will append component names */
228 (void) strcpy(subpath
, path
);
229 if (subpath
[0] != '\0' && subpath
[n
-1] != '/')
231 component
= &subpath
[n
];
232 /* LINTED: result will fit into a 32-bit int */
233 sublen
-= component
- subpath
;
236 * Read the directory one component at a time.
237 * We must ignore "." and "..", but other than that,
238 * just create a path name and call self to check it out.
240 while ((dp
= readdir(dirp
)) != NULL
) {
241 if (strcmp(dp
->d_name
, ".") != 0 &&
242 strcmp(dp
->d_name
, "..") != 0) {
245 /* Append component name to the working path */
246 (void) strncpy(component
, dp
->d_name
, sublen
);
247 component
[sublen
- 1] = '\0';
250 * If we are about to exceed our depth,
251 * remember where we are and close a file.
254 here
= telldir(dirp
);
255 (void) closedir(dirp
);
259 * Do a recursive call to process the file.
260 * (watch this, sports fans)
262 rc
= lf_xftw(subpath
, fn
, depth
-1, statfn
);
266 (void) closedir(dirp
);
271 * If we closed the file, try to reopen it.
274 dirp
= opendir(path
);
285 * We got out of the subdirectory loop. The return from
286 * the final readdir is in dp. Clean up.
289 (void) closedir(dirp
);