xref: /illumos-gate/usr/src/cmd/backup/dump/lftw.c (revision 88f8b78a88cbdc6d8c1af5c3e54bc49d25095c98)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
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.
13  *
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]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1998,2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 /*	Copyright (c) 1988 AT&T	*/
28 /*	  All Rights Reserved	*/
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 /* LINTLIBRARY */
33 /*
34  *	ftw - file tree walk
35  *
36  *	int ftw (path, fn, depth)  char *path; int (*fn)(); int depth;
37  *
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:
47  *
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.
51  *
52  *		FTW_D	The object is a directory for which stat and
53  *			open for read were both successful.
54  *
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.
59  *
60  *		FTW_NS	Stat failed on the object because of lack of
61  *			appropriate permission.  This indication will
62  *			be given, for example, for each file in a
63  *			directory with read but no execute permission.
64  *			Because stat failed, it is not possible to
65  *			determine whether this object is a file or a
66  *			directory.  The stat buffer passed to fn will
67  *			contain garbage.  Stat failure for any reason
68  *			other than lack of permission will be
69  *			considered an error and will cause ftw to stop
70  *			and return -1 to its caller.
71  *
72  *	If fn returns nonzero, ftw stops and returns the same value
73  *	to its caller.  If ftw gets into other trouble along the way,
74  *	it returns -1 and leaves an indication of the cause in errno.
75  *
76  *	The third argument to ftw does not limit the depth to which
77  *	ftw will go.  Rather, it limits the depth to which ftw will
78  *	go before it starts recycling file descriptors.  In general,
79  *	it is necessary to use a file descriptor for each level of the
80  *	tree, but they can be recycled for deep trees by saving the
81  *	position, closing, re-opening, and seeking.  It is possible
82  *	to start recycling file descriptors by sensing when we have
83  *	run out, but in general this will not be terribly useful if
84  *	fn expects to be able to open files.  We could also figure out
85  *	how many file descriptors are available and guarantee a certain
86  *	number to fn, but we would not know how many to guarantee,
87  *	and we do not want to impose the extra overhead on a caller who
88  *	knows how many are available without having to figure it out.
89  *
90  *	It is possible for ftw to die with a memory fault in the event
91  *	of a file system so deeply nested that the stack overflows.
92  */
93 
94 #include <sys/fs/ufs_inode.h>
95 #include <sys/types.h>
96 #include <sys/stat.h>
97 #include <dirent.h>
98 #include <errno.h>
99 #include <malloc.h>
100 #include <string.h>
101 #include <fcntl.h>
102 #include <unistd.h>
103 #include <ftw.h>
104 
105 #define	NULL 0
106 
107 static int pwdfd;
108 
109 #ifdef __STDC__
110 static int lf_xftw(
111 	const char *,
112 	int (*)(const char *, const struct stat64 *, int),
113 	int,
114 	int (*)(const char *, struct stat64 *));
115 #else
116 static int lf_xftw();
117 #endif
118 
119 #ifdef __STDC__
120 lf_lftw(
121 	const char *path,
122 	int (*fn)(const char *, const struct stat64 *, int),
123 	int depth)
124 #else
125 lf_lftw(path, fn, depth)
126 	char	*path;
127 	int	(*fn)();
128 	int depth;
129 #endif
130 {
131 	int rc;
132 
133 	if ((pwdfd = open(".", O_RDONLY)) < 0) {
134 		return (-1);
135 	} else {
136 		rc = (lf_xftw(path, fn, depth, lstat64));
137 		(void) close(pwdfd);
138 		return (rc);
139 	}
140 }
141 
142 static int
143 #ifdef __STDC__
144 lf_xftw(
145 	const char *path,
146 	int (*fn)(const char *, const struct stat64 *, int),
147 	int depth,
148 	int (*statfn)(const char *, struct stat64 *))
149 #else
150 lf_xftw(path, fn, depth, statfn)
151 	char	*path;
152 	int	(*fn)();
153 	int	depth;
154 	int	(*statfn)();
155 #endif
156 {
157 	int n;
158 	int rc, sublen, saverr, attrfd;
159 	DIR *dirp;
160 	char *subpath, *component;
161 	struct stat64 sb;
162 	struct dirent *dp;
163 	extern dev_t partial_dev;
164 
165 	/*
166 	 * Try to get file status.
167 	 * If unsuccessful, errno will say why.
168 	 */
169 	if ((*statfn)(path, &sb) < 0)
170 		return (errno == EACCES? (*fn)(path, &sb, FTW_NS): -1);
171 	/*
172 	 *	The stat succeeded, so we know the object exists.
173 	 *	Make sure it is not a mount point for another filesystem.
174 	 *	The following check must be made here because:
175 	 *
176 	 *		+ namefs can be mounted on anything, but a directory
177 	 *		+ all other filesystems must be mounted on a directory
178 	 */
179 	if (sb.st_dev != partial_dev) {
180 		return (0);
181 	}
182 	/*
183 	 *	Check for presence of attributes on file
184 	 */
185 	if (pathconf(path, _PC_XATTR_EXISTS) == 1) {
186 		attrfd = attropen64(path, ".", O_RDONLY|O_NONBLOCK);
187 	} else {
188 		attrfd = -1;
189 	}
190 	/*
191 	 *	If not a directory, call the user function and return.
192 	 */
193 	if ((sb.st_mode & S_IFMT) != S_IFDIR &&
194 	    (sb.st_mode & IFMT) != IFATTRDIR) {
195 		rc = (*fn)(path, &sb, FTW_F);
196 		if (rc == 0 && attrfd != -1) {
197 			(void) fchdir(attrfd);
198 			rc = lf_xftw(".", fn, depth-1, statfn);
199 			(void) fchdir(pwdfd);
200 			(void) close(attrfd);
201 		}
202 		return (rc);
203 	}
204 	/*
205 	 *	The object was a directory and not a mount point.
206 	 *
207 	 *	Open a file to read the directory
208 	 */
209 	dirp = opendir(path);
210 
211 	/*
212 	 *	Call the user function, telling it whether
213 	 *	the directory can be read.  If it can't be read
214 	 *	call the user function or indicate an error,
215 	 *	depending on the reason it couldn't be read.
216 	 */
217 	if (dirp == NULL)
218 		rc = (errno == EACCES? (*fn)(path, &sb, FTW_DNR): -1);
219 	else
220 		rc = (*fn)(path, &sb, FTW_D);
221 	/*
222 	 *	If the directory has attributes, process the
223 	 *	attributes before processing the directory contents.
224 	 */
225 	if (rc == 0 && attrfd != -1) {
226 		(void) fchdir(attrfd);
227 		rc = lf_xftw(".", fn, depth-1, statfn);
228 		(void) fchdir(pwdfd);
229 		(void) close(attrfd);
230 	}
231 	if (rc != 0 || dirp == NULL)
232 		return (rc);
233 
234 	/* Allocate a buffer to hold generated pathnames. */
235 	/* LINTED: the length will fit into a signed integer */
236 	n = (int)strlen(path);
237 	sublen = n + MAXNAMLEN + 1; /* +1 for appended / */
238 	subpath = malloc((unsigned)(sublen+1));	/* +1 for NUL */
239 	if (subpath == NULL) {
240 		saverr = errno;
241 		(void) closedir(dirp);
242 		errno = saverr;
243 		return (-1);
244 	}
245 
246 	/* Create a prefix to which we will append component names */
247 	(void) strcpy(subpath, path);
248 	if (subpath[0] != '\0' && subpath[n-1] != '/')
249 		subpath[n++] = '/';
250 	component = &subpath[n];
251 	/* LINTED: result will fit into a 32-bit int */
252 	sublen -= component - subpath;
253 
254 	/*
255 	 *	Read the directory one component at a time.
256 	 *	We must ignore "." and "..", but other than that,
257 	 *	just create a path name and call self to check it out.
258 	 */
259 	while ((dp = readdir(dirp)) != NULL) {
260 		if (strcmp(dp->d_name, ".") != 0 &&
261 		    strcmp(dp->d_name, "..") != 0) {
262 			long here;
263 
264 			/* Append component name to the working path */
265 			(void) strncpy(component, dp->d_name, sublen);
266 			component[sublen - 1] = '\0';
267 
268 			/*
269 			 *	If we are about to exceed our depth,
270 			 *	remember where we are and close a file.
271 			 */
272 			if (depth <= 1) {
273 				here = telldir(dirp);
274 				(void) closedir(dirp);
275 			}
276 
277 			/*
278 			 *	Do a recursive call to process the file.
279 			 *	(watch this, sports fans)
280 			 */
281 			rc = lf_xftw(subpath, fn, depth-1, statfn);
282 			if (rc != 0) {
283 				free(subpath);
284 				if (depth > 1)
285 					(void) closedir(dirp);
286 				return (rc);
287 			}
288 
289 			/*
290 			 *	If we closed the file, try to reopen it.
291 			 */
292 			if (depth <= 1) {
293 				dirp = opendir(path);
294 				if (dirp == NULL) {
295 					free(subpath);
296 					return (-1);
297 				}
298 				seekdir(dirp, here);
299 			}
300 		}
301 	}
302 
303 	/*
304 	 *	We got out of the subdirectory loop.  The return from
305 	 *	the final readdir is in dp.  Clean up.
306 	 */
307 	free(subpath);
308 	(void) closedir(dirp);
309 	return (0);
310 }
311