1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright (c) 1988 AT&T */
27 /* All Rights Reserved */
28
29 /* LINTLIBRARY */
30 /*
31 * ftw - file tree walk
32 *
33 * int ftw (path, fn, depth) char *path; int (*fn)(); int depth;
34 *
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:
44 *
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.
48 *
49 * FTW_D The object is a directory for which stat and
50 * open for read were both successful.
51 *
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.
56 *
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.
68 *
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.
72 *
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.
86 *
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.
89 */
90
91 #include <sys/fs/ufs_inode.h>
92 #include <sys/types.h>
93 #include <sys/stat.h>
94 #include <dirent.h>
95 #include <errno.h>
96 #include <malloc.h>
97 #include <string.h>
98 #include <fcntl.h>
99 #include <unistd.h>
100 #include <ftw.h>
101
102 static int pwdfd;
103
104 static int lf_xftw(
105 const char *,
106 int (*)(const char *, const struct stat64 *, int),
107 int,
108 int (*)(const char *, struct stat64 *));
109
110 int
lf_lftw(const char * path,int (* fn)(const char *,const struct stat64 *,int),int depth)111 lf_lftw(
112 const char *path,
113 int (*fn)(const char *, const struct stat64 *, int),
114 int depth)
115 {
116 int rc;
117
118 if ((pwdfd = open(".", O_RDONLY)) < 0) {
119 return (-1);
120 } else {
121 rc = (lf_xftw(path, fn, depth, lstat64));
122 (void) close(pwdfd);
123 return (rc);
124 }
125 }
126
127 static int
128 #ifdef __STDC__
lf_xftw(const char * path,int (* fn)(const char *,const struct stat64 *,int),int depth,int (* statfn)(const char *,struct stat64 *))129 lf_xftw(
130 const char *path,
131 int (*fn)(const char *, const struct stat64 *, int),
132 int depth,
133 int (*statfn)(const char *, struct stat64 *))
134 #else
135 lf_xftw(char *path, int (*fn)(), int depth, int (*statfn)())
136 #endif
137 {
138 int n;
139 int rc, sublen, saverr, attrfd;
140 DIR *dirp;
141 char *subpath, *component;
142 struct stat64 sb;
143 struct dirent *dp;
144 extern dev_t partial_dev;
145
146 /*
147 * Try to get file status.
148 * If unsuccessful, errno will say why.
149 */
150 if ((*statfn)(path, &sb) < 0)
151 return (errno == EACCES? (*fn)(path, &sb, FTW_NS): -1);
152 /*
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:
156 *
157 * + namefs can be mounted on anything, but a directory
158 * + all other filesystems must be mounted on a directory
159 */
160 if (sb.st_dev != partial_dev) {
161 return (0);
162 }
163 /*
164 * Check for presence of attributes on file
165 */
166 if (pathconf(path, _PC_XATTR_EXISTS) == 1) {
167 attrfd = attropen64(path, ".", O_RDONLY|O_NONBLOCK);
168 } else {
169 attrfd = -1;
170 }
171 /*
172 * If not a directory, call the user function and return.
173 */
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);
182 }
183 return (rc);
184 }
185 /*
186 * The object was a directory and not a mount point.
187 *
188 * Open a file to read the directory
189 */
190 dirp = opendir(path);
191
192 /*
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.
197 */
198 if (dirp == NULL)
199 rc = (errno == EACCES? (*fn)(path, &sb, FTW_DNR): -1);
200 else
201 rc = (*fn)(path, &sb, FTW_D);
202 /*
203 * If the directory has attributes, process the
204 * attributes before processing the directory contents.
205 */
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);
211 }
212 if (rc != 0 || dirp == NULL)
213 return (rc);
214
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) {
221 saverr = errno;
222 (void) closedir(dirp);
223 errno = saverr;
224 return (-1);
225 }
226
227 /* Create a prefix to which we will append component names */
228 (void) strcpy(subpath, path);
229 if (subpath[0] != '\0' && subpath[n-1] != '/')
230 subpath[n++] = '/';
231 component = &subpath[n];
232 /* LINTED: result will fit into a 32-bit int */
233 sublen -= component - subpath;
234
235 /*
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.
239 */
240 while ((dp = readdir(dirp)) != NULL) {
241 if (strcmp(dp->d_name, ".") != 0 &&
242 strcmp(dp->d_name, "..") != 0) {
243 long here;
244
245 /* Append component name to the working path */
246 (void) strncpy(component, dp->d_name, sublen);
247 component[sublen - 1] = '\0';
248
249 /*
250 * If we are about to exceed our depth,
251 * remember where we are and close a file.
252 */
253 if (depth <= 1) {
254 here = telldir(dirp);
255 (void) closedir(dirp);
256 }
257
258 /*
259 * Do a recursive call to process the file.
260 * (watch this, sports fans)
261 */
262 rc = lf_xftw(subpath, fn, depth-1, statfn);
263 if (rc != 0) {
264 free(subpath);
265 if (depth > 1)
266 (void) closedir(dirp);
267 return (rc);
268 }
269
270 /*
271 * If we closed the file, try to reopen it.
272 */
273 if (depth <= 1) {
274 dirp = opendir(path);
275 if (dirp == NULL) {
276 free(subpath);
277 return (-1);
278 }
279 seekdir(dirp, here);
280 }
281 }
282 }
283
284 /*
285 * We got out of the subdirectory loop. The return from
286 * the final readdir is in dp. Clean up.
287 */
288 free(subpath);
289 (void) closedir(dirp);
290 return (0);
291 }
292