xref: /illumos-gate/usr/src/cmd/backup/dump/lftw.c (revision 15c07adc1c7b828006b5e3c4d528b92229d6bd23)
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
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__
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