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