Browse code

clamav: add private fts() implementation

This file has been copied out of glibc and slightly modified.

The fts() implementation in current libc does not support LFS which
looks like a glibc (only) restriction. The code has been copied out of
glibc and the used functions (like fstat()) have been modified to use the
public exported libc function instead of the internal one. The libc
functions are are using the LFS version.
The fts() functions gained the _priv_ prefix so they do not clash with
glibc's functions.

This patch will be dropped once glibc's fts() implementations is LFS
safe which is glibc 2.23+, the bug is tracked in glibc's bugzilla as
11460 => https://sourceware.org/bugzilla/show_bug.cgi?id=11460

Patch-Name: clamav_add_private_fts_implementation.patch
Signed-off-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
Signed-off-by: Mickey Sola <msola@sourcefire.com>

Sebastian Andrzej Siewior authored on 2015/11/17 06:19:16
Showing 8 changed files
... ...
@@ -54,7 +54,12 @@ clamd_SOURCES = \
54 54
     onaccess_hash.c \
55 55
     onaccess_hash.h \
56 56
     onaccess_scth.c \
57
-    onaccess_scth.h
57
+    onaccess_scth.h \
58
+    priv_fts.h
59
+
60
+if !SYSTEM_LFS_FTS
61
+clamd_SOURCES += fts.c
62
+endif
58 63
 
59 64
 AM_CFLAGS=@WERR_CFLAGS@
60 65
 
61 66
new file mode 100644
... ...
@@ -0,0 +1,1158 @@
0
+/* File tree traversal functions.
1
+   Copyright (C) 1994-2015 Free Software Foundation, Inc.
2
+   This file is part of the GNU C Library.
3
+
4
+   The GNU C Library is free software; you can redistribute it and/or
5
+   modify it under the terms of the GNU Lesser General Public
6
+   License as published by the Free Software Foundation; either
7
+   version 2.1 of the License, or (at your option) any later version.
8
+
9
+   The GNU C Library is distributed in the hope that it will be useful,
10
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+   Lesser General Public License for more details.
13
+
14
+   You should have received a copy of the GNU Lesser General Public
15
+   License along with the GNU C Library; if not, see
16
+   <http://www.gnu.org/licenses/>.  */
17
+
18
+/*-
19
+ * Copyright (c) 1990, 1993, 1994
20
+ *	The Regents of the University of California.  All rights reserved.
21
+ *
22
+ * Redistribution and use in source and binary forms, with or without
23
+ * modification, are permitted provided that the following conditions
24
+ * are met:
25
+ * 1. Redistributions of source code must retain the above copyright
26
+ *    notice, this list of conditions and the following disclaimer.
27
+ * 2. Redistributions in binary form must reproduce the above copyright
28
+ *    notice, this list of conditions and the following disclaimer in the
29
+ *    documentation and/or other materials provided with the distribution.
30
+ * 4. Neither the name of the University nor the names of its contributors
31
+ *    may be used to endorse or promote products derived from this software
32
+ *    without specific prior written permission.
33
+ *
34
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
35
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44
+ * SUCH DAMAGE.
45
+ */
46
+
47
+#if defined(LIBC_SCCS) && !defined(lint)
48
+static char sccsid[] = "@(#)fts.c	8.6 (Berkeley) 8/14/94";
49
+#endif /* LIBC_SCCS and not lint */
50
+
51
+#include <sys/param.h>
52
+#include <sys/stat.h>
53
+#include <fcntl.h>
54
+#include <dirent.h>
55
+#include <errno.h>
56
+#include "priv_fts.h"
57
+#include <stdlib.h>
58
+#include <string.h>
59
+#include <unistd.h>
60
+
61
+#define FTS_OPEN _priv_fts_open
62
+#define FTS_CLOSE _priv_fts_close
63
+#define FTS_READ _priv_fts_read
64
+#define FTS_SET _priv_fts_set
65
+#define FTS_CHILDREN _priv_fts_children
66
+#define FTSOBJ FTS
67
+#define FTSENTRY FTSENT
68
+#define INO_T ino_t
69
+#define STAT stat
70
+#define LSTAT lstat
71
+
72
+#define internal_function
73
+# define __set_errno(val) (errno = (val))
74
+
75
+/* Largest alignment size needed, minus one.
76
+   Usually long double is the worst case.  */
77
+#ifndef ALIGNBYTES
78
+#define ALIGNBYTES	(__alignof__ (long double) - 1)
79
+#endif
80
+/* Align P to that size.  */
81
+#ifndef ALIGN
82
+#define	ALIGN(p)	(((unsigned long int) (p) + ALIGNBYTES) & ~ALIGNBYTES)
83
+#endif
84
+
85
+/* Support for the LFS API version.  */
86
+#ifndef FTS_OPEN
87
+#define FTS_OPEN fts_open
88
+#define FTS_CLOSE fts_close
89
+#define FTS_READ fts_read
90
+#define FTS_SET fts_set
91
+#define FTS_CHILDREN fts_children
92
+# define FTSOBJ FTS
93
+# define FTSENTRY FTSENT
94
+# define INO_T ino_t
95
+# define STAT stat
96
+# define LSTAT lstat
97
+#endif
98
+
99
+static FTSENTRY	*fts_alloc (FTSOBJ *, const char *, size_t) internal_function;
100
+static FTSENTRY	*fts_build (FTSOBJ *, int) internal_function;
101
+static void	 fts_lfree (FTSENTRY *) internal_function;
102
+static void	 fts_load (FTSOBJ *, FTSENTRY *) internal_function;
103
+static size_t	 fts_maxarglen (char * const *) internal_function;
104
+static void	 fts_padjust (FTSOBJ *, FTSENTRY *) internal_function;
105
+static int	 fts_palloc (FTSOBJ *, size_t) internal_function;
106
+static FTSENTRY	*fts_sort (FTSOBJ *, FTSENTRY *, int) internal_function;
107
+static u_short	 fts_stat (FTSOBJ *, FTSENTRY *, int) internal_function;
108
+static int      fts_safe_changedir (FTSOBJ *, FTSENTRY *, int, const char *)
109
+     internal_function;
110
+
111
+#ifndef MAX
112
+#define MAX(a, b)	({ __typeof__ (a) _a = (a); \
113
+			   __typeof__ (b) _b = (b); \
114
+			   _a > _b ? _a : _b; })
115
+#endif
116
+
117
+#define	ISDOT(a)	(a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
118
+
119
+#define CLR(opt)	(sp->fts_options &= ~(opt))
120
+#define	ISSET(opt)	(sp->fts_options & (opt))
121
+#define	SET(opt)	(sp->fts_options |= (opt))
122
+
123
+#define	FCHDIR(sp, fd)	(!ISSET(FTS_NOCHDIR) && fchdir(fd))
124
+
125
+/* fts_build flags */
126
+#define	BCHILD		1		/* fts_children */
127
+#define	BNAMES		2		/* fts_children, names only */
128
+#define	BREAD		3		/* fts_read */
129
+
130
+FTSOBJ *
131
+FTS_OPEN (char * const *argv, int options,
132
+	  int (*compar) (const FTSENTRY **, const FTSENTRY **))
133
+{
134
+	FTSOBJ *sp;
135
+	FTSENTRY *p, *root;
136
+	int nitems;
137
+	FTSENTRY *parent = NULL;
138
+	FTSENTRY *tmp;
139
+
140
+	/* Options check. */
141
+	if (options & ~FTS_OPTIONMASK) {
142
+		__set_errno (EINVAL);
143
+		return (NULL);
144
+	}
145
+
146
+	/* Allocate/initialize the stream */
147
+	if ((sp = malloc((u_int)sizeof(FTSOBJ))) == NULL)
148
+		return (NULL);
149
+	memset(sp, 0, sizeof(FTSOBJ));
150
+	sp->fts_compar = (int (*) (const void *, const void *)) compar;
151
+	sp->fts_options = options;
152
+
153
+	/* Logical walks turn on NOCHDIR; symbolic links are too hard. */
154
+	if (ISSET(FTS_LOGICAL))
155
+		SET(FTS_NOCHDIR);
156
+
157
+	/*
158
+	 * Start out with 1K of path space, and enough, in any case,
159
+	 * to hold the user's paths.
160
+	 */
161
+#ifndef MAXPATHLEN
162
+#define MAXPATHLEN 1024
163
+#endif
164
+	size_t maxarglen = fts_maxarglen(argv);
165
+	if (fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))
166
+		goto mem1;
167
+
168
+	/* Allocate/initialize root's parent. */
169
+	if (*argv != NULL) {
170
+		if ((parent = fts_alloc(sp, "", 0)) == NULL)
171
+			goto mem2;
172
+		parent->fts_level = FTS_ROOTPARENTLEVEL;
173
+	  }
174
+
175
+	/* Allocate/initialize root(s). */
176
+	for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
177
+		/* Don't allow zero-length paths. */
178
+		size_t len = strlen(*argv);
179
+		if (len == 0) {
180
+			__set_errno (ENOENT);
181
+			goto mem3;
182
+		}
183
+
184
+		p = fts_alloc(sp, *argv, len);
185
+		p->fts_level = FTS_ROOTLEVEL;
186
+		p->fts_parent = parent;
187
+		p->fts_accpath = p->fts_name;
188
+		p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));
189
+
190
+		/* Command-line "." and ".." are real directories. */
191
+		if (p->fts_info == FTS_DOT)
192
+			p->fts_info = FTS_D;
193
+
194
+		/*
195
+		 * If comparison routine supplied, traverse in sorted
196
+		 * order; otherwise traverse in the order specified.
197
+		 */
198
+		if (compar) {
199
+			p->fts_link = root;
200
+			root = p;
201
+		} else {
202
+			p->fts_link = NULL;
203
+			if (root == NULL)
204
+				tmp = root = p;
205
+			else {
206
+				tmp->fts_link = p;
207
+				tmp = p;
208
+			}
209
+		}
210
+	}
211
+	if (compar && nitems > 1)
212
+		root = fts_sort(sp, root, nitems);
213
+
214
+	/*
215
+	 * Allocate a dummy pointer and make fts_read think that we've just
216
+	 * finished the node before the root(s); set p->fts_info to FTS_INIT
217
+	 * so that everything about the "current" node is ignored.
218
+	 */
219
+	if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
220
+		goto mem3;
221
+	sp->fts_cur->fts_link = root;
222
+	sp->fts_cur->fts_info = FTS_INIT;
223
+
224
+	/*
225
+	 * If using chdir(2), grab a file descriptor pointing to dot to ensure
226
+	 * that we can get back here; this could be avoided for some paths,
227
+	 * but almost certainly not worth the effort.  Slashes, symbolic links,
228
+	 * and ".." are all fairly nasty problems.  Note, if we can't get the
229
+	 * descriptor we run anyway, just more slowly.
230
+	 */
231
+	if (!ISSET(FTS_NOCHDIR)
232
+	    && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0)
233
+		SET(FTS_NOCHDIR);
234
+
235
+	return (sp);
236
+
237
+mem3:	fts_lfree(root);
238
+	free(parent);
239
+mem2:	free(sp->fts_path);
240
+mem1:	free(sp);
241
+	return (NULL);
242
+}
243
+
244
+static void
245
+internal_function
246
+fts_load (FTSOBJ *sp, FTSENTRY *p)
247
+{
248
+	int len;
249
+	char *cp;
250
+
251
+	/*
252
+	 * Load the stream structure for the next traversal.  Since we don't
253
+	 * actually enter the directory until after the preorder visit, set
254
+	 * the fts_accpath field specially so the chdir gets done to the right
255
+	 * place and the user can access the first node.  From fts_open it's
256
+	 * known that the path will fit.
257
+	 */
258
+	len = p->fts_pathlen = p->fts_namelen;
259
+	memmove(sp->fts_path, p->fts_name, len + 1);
260
+	if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
261
+		len = strlen(++cp);
262
+		memmove(p->fts_name, cp, len + 1);
263
+		p->fts_namelen = len;
264
+	}
265
+	p->fts_accpath = p->fts_path = sp->fts_path;
266
+	sp->fts_dev = p->fts_dev;
267
+}
268
+
269
+int
270
+FTS_CLOSE (FTSOBJ *sp)
271
+{
272
+	FTSENTRY *freep, *p;
273
+	int saved_errno;
274
+
275
+	/*
276
+	 * This still works if we haven't read anything -- the dummy structure
277
+	 * points to the root list, so we step through to the end of the root
278
+	 * list which has a valid parent pointer.
279
+	 */
280
+	if (sp->fts_cur) {
281
+		for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
282
+			freep = p;
283
+			p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
284
+			free(freep);
285
+		}
286
+		free(p);
287
+	}
288
+
289
+	/* Free up child linked list, sort array, path buffer. */
290
+	if (sp->fts_child)
291
+		fts_lfree(sp->fts_child);
292
+	free(sp->fts_array);
293
+	free(sp->fts_path);
294
+
295
+	/* Return to original directory, save errno if necessary. */
296
+	if (!ISSET(FTS_NOCHDIR)) {
297
+		saved_errno = fchdir(sp->fts_rfd) ? errno : 0;
298
+		(void)close(sp->fts_rfd);
299
+
300
+		/* Set errno and return. */
301
+		if (saved_errno != 0) {
302
+			/* Free up the stream pointer. */
303
+			free(sp);
304
+			__set_errno (saved_errno);
305
+			return (-1);
306
+		}
307
+	}
308
+
309
+	/* Free up the stream pointer. */
310
+	free(sp);
311
+	return (0);
312
+}
313
+
314
+/*
315
+ * Special case of "/" at the end of the path so that slashes aren't
316
+ * appended which would cause paths to be written as "....//foo".
317
+ */
318
+#define	NAPPEND(p)							\
319
+	(p->fts_path[p->fts_pathlen - 1] == '/'				\
320
+	    ? p->fts_pathlen - 1 : p->fts_pathlen)
321
+
322
+FTSENTRY *
323
+FTS_READ (FTSOBJ *sp)
324
+{
325
+	FTSENTRY *p, *tmp;
326
+	int instr;
327
+	char *t;
328
+	int saved_errno;
329
+
330
+	/* If finished or unrecoverable error, return NULL. */
331
+	if (sp->fts_cur == NULL || ISSET(FTS_STOP))
332
+		return (NULL);
333
+
334
+	/* Set current node pointer. */
335
+	p = sp->fts_cur;
336
+
337
+	/* Save and zero out user instructions. */
338
+	instr = p->fts_instr;
339
+	p->fts_instr = FTS_NOINSTR;
340
+
341
+	/* Any type of file may be re-visited; re-stat and re-turn. */
342
+	if (instr == FTS_AGAIN) {
343
+		p->fts_info = fts_stat(sp, p, 0);
344
+		return (p);
345
+	}
346
+
347
+	/*
348
+	 * Following a symlink -- SLNONE test allows application to see
349
+	 * SLNONE and recover.  If indirecting through a symlink, have
350
+	 * keep a pointer to current location.  If unable to get that
351
+	 * pointer, follow fails.
352
+	 */
353
+	if (instr == FTS_FOLLOW &&
354
+	    (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
355
+		p->fts_info = fts_stat(sp, p, 1);
356
+		if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
357
+			if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) {
358
+				p->fts_errno = errno;
359
+				p->fts_info = FTS_ERR;
360
+			} else
361
+				p->fts_flags |= FTS_SYMFOLLOW;
362
+		}
363
+		return (p);
364
+	}
365
+
366
+	/* Directory in pre-order. */
367
+	if (p->fts_info == FTS_D) {
368
+		/* If skipped or crossed mount point, do post-order visit. */
369
+		if (instr == FTS_SKIP ||
370
+		    (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
371
+			if (p->fts_flags & FTS_SYMFOLLOW)
372
+				(void)close(p->fts_symfd);
373
+			if (sp->fts_child) {
374
+				fts_lfree(sp->fts_child);
375
+				sp->fts_child = NULL;
376
+			}
377
+			p->fts_info = FTS_DP;
378
+			return (p);
379
+		}
380
+
381
+		/* Rebuild if only read the names and now traversing. */
382
+		if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
383
+			CLR(FTS_NAMEONLY);
384
+			fts_lfree(sp->fts_child);
385
+			sp->fts_child = NULL;
386
+		}
387
+
388
+		/*
389
+		 * Cd to the subdirectory.
390
+		 *
391
+		 * If have already read and now fail to chdir, whack the list
392
+		 * to make the names come out right, and set the parent errno
393
+		 * so the application will eventually get an error condition.
394
+		 * Set the FTS_DONTCHDIR flag so that when we logically change
395
+		 * directories back to the parent we don't do a chdir.
396
+		 *
397
+		 * If haven't read do so.  If the read fails, fts_build sets
398
+		 * FTS_STOP or the fts_info field of the node.
399
+		 */
400
+		if (sp->fts_child != NULL) {
401
+			if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
402
+				p->fts_errno = errno;
403
+				p->fts_flags |= FTS_DONTCHDIR;
404
+				for (p = sp->fts_child; p != NULL;
405
+				     p = p->fts_link)
406
+					p->fts_accpath =
407
+					    p->fts_parent->fts_accpath;
408
+			}
409
+		} else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
410
+			if (ISSET(FTS_STOP))
411
+				return (NULL);
412
+			return (p);
413
+		}
414
+		p = sp->fts_child;
415
+		sp->fts_child = NULL;
416
+		sp->fts_cur = p;
417
+		goto name;
418
+	}
419
+
420
+	/* Move to the next node on this level. */
421
+next:	tmp = p;
422
+	if ((p = p->fts_link) != NULL) {
423
+		sp->fts_cur = p;
424
+		free(tmp);
425
+
426
+		/*
427
+		 * If reached the top, return to the original directory (or
428
+		 * the root of the tree), and load the paths for the next root.
429
+		 */
430
+		if (p->fts_level == FTS_ROOTLEVEL) {
431
+			if (FCHDIR(sp, sp->fts_rfd)) {
432
+				SET(FTS_STOP);
433
+				return (NULL);
434
+			}
435
+			fts_load(sp, p);
436
+			return p;
437
+		}
438
+
439
+		/*
440
+		 * User may have called fts_set on the node.  If skipped,
441
+		 * ignore.  If followed, get a file descriptor so we can
442
+		 * get back if necessary.
443
+		 */
444
+		if (p->fts_instr == FTS_SKIP)
445
+			goto next;
446
+		if (p->fts_instr == FTS_FOLLOW) {
447
+			p->fts_info = fts_stat(sp, p, 1);
448
+			if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
449
+				if ((p->fts_symfd =
450
+				    open(".", O_RDONLY, 0)) < 0) {
451
+					p->fts_errno = errno;
452
+					p->fts_info = FTS_ERR;
453
+				} else
454
+					p->fts_flags |= FTS_SYMFOLLOW;
455
+			}
456
+			p->fts_instr = FTS_NOINSTR;
457
+		}
458
+
459
+name:		t = sp->fts_path + NAPPEND(p->fts_parent);
460
+		*t++ = '/';
461
+		memmove(t, p->fts_name, p->fts_namelen + 1);
462
+		return p;
463
+	}
464
+
465
+	/* Move up to the parent node. */
466
+	p = tmp->fts_parent;
467
+	sp->fts_cur = p;
468
+	free(tmp);
469
+
470
+	if (p->fts_level == FTS_ROOTPARENTLEVEL) {
471
+		/*
472
+		 * Done; free everything up and set errno to 0 so the user
473
+		 * can distinguish between error and EOF.
474
+		 */
475
+		free(p);
476
+		__set_errno (0);
477
+		return (sp->fts_cur = NULL);
478
+	}
479
+
480
+	/* NUL terminate the pathname. */
481
+	sp->fts_path[p->fts_pathlen] = '\0';
482
+
483
+	/*
484
+	 * Return to the parent directory.  If at a root node or came through
485
+	 * a symlink, go back through the file descriptor.  Otherwise, cd up
486
+	 * one directory.
487
+	 */
488
+	if (p->fts_level == FTS_ROOTLEVEL) {
489
+		if (FCHDIR(sp, sp->fts_rfd)) {
490
+			SET(FTS_STOP);
491
+			return (NULL);
492
+		}
493
+	} else if (p->fts_flags & FTS_SYMFOLLOW) {
494
+		if (FCHDIR(sp, p->fts_symfd)) {
495
+			saved_errno = errno;
496
+			(void)close(p->fts_symfd);
497
+			__set_errno (saved_errno);
498
+			SET(FTS_STOP);
499
+			return (NULL);
500
+		}
501
+		(void)close(p->fts_symfd);
502
+	} else if (!(p->fts_flags & FTS_DONTCHDIR) &&
503
+		   fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
504
+		SET(FTS_STOP);
505
+		return (NULL);
506
+	}
507
+	p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
508
+	return p;
509
+}
510
+
511
+/*
512
+ * Fts_set takes the stream as an argument although it's not used in this
513
+ * implementation; it would be necessary if anyone wanted to add global
514
+ * semantics to fts using fts_set.  An error return is allowed for similar
515
+ * reasons.
516
+ */
517
+/* ARGSUSED */
518
+int
519
+FTS_SET (FTSOBJ *sp, FTSENTRY *p, int instr)
520
+{
521
+	if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
522
+	    instr != FTS_NOINSTR && instr != FTS_SKIP) {
523
+		__set_errno (EINVAL);
524
+		return (1);
525
+	}
526
+	p->fts_instr = instr;
527
+	return (0);
528
+}
529
+
530
+FTSENTRY *
531
+FTS_CHILDREN(FTSOBJ *sp, int instr)
532
+{
533
+	FTSENTRY *p;
534
+	int fd;
535
+
536
+	if (instr != 0 && instr != FTS_NAMEONLY) {
537
+		__set_errno (EINVAL);
538
+		return (NULL);
539
+	}
540
+
541
+	/* Set current node pointer. */
542
+	p = sp->fts_cur;
543
+
544
+	/*
545
+	 * Errno set to 0 so user can distinguish empty directory from
546
+	 * an error.
547
+	 */
548
+	__set_errno (0);
549
+
550
+	/* Fatal errors stop here. */
551
+	if (ISSET(FTS_STOP))
552
+		return (NULL);
553
+
554
+	/* Return logical hierarchy of user's arguments. */
555
+	if (p->fts_info == FTS_INIT)
556
+		return (p->fts_link);
557
+
558
+	/*
559
+	 * If not a directory being visited in pre-order, stop here.  Could
560
+	 * allow FTS_DNR, assuming the user has fixed the problem, but the
561
+	 * same effect is available with FTS_AGAIN.
562
+	 */
563
+	if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
564
+		return (NULL);
565
+
566
+	/* Free up any previous child list. */
567
+	if (sp->fts_child != NULL)
568
+		fts_lfree(sp->fts_child);
569
+
570
+	if (instr == FTS_NAMEONLY) {
571
+		SET(FTS_NAMEONLY);
572
+		instr = BNAMES;
573
+	} else
574
+		instr = BCHILD;
575
+
576
+	/*
577
+	 * If using chdir on a relative path and called BEFORE fts_read does
578
+	 * its chdir to the root of a traversal, we can lose -- we need to
579
+	 * chdir into the subdirectory, and we don't know where the current
580
+	 * directory is, so we can't get back so that the upcoming chdir by
581
+	 * fts_read will work.
582
+	 */
583
+	if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
584
+	    ISSET(FTS_NOCHDIR))
585
+		return (sp->fts_child = fts_build(sp, instr));
586
+
587
+	if ((fd = open(".", O_RDONLY, 0)) < 0)
588
+		return (NULL);
589
+	sp->fts_child = fts_build(sp, instr);
590
+	if (fchdir(fd))
591
+		return (NULL);
592
+	(void)close(fd);
593
+	return (sp->fts_child);
594
+}
595
+
596
+static inline int
597
+dirent_not_directory(const struct dirent *dp)
598
+{
599
+#if defined DT_DIR && defined _DIRENT_HAVE_D_TYPE
600
+        return dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN;
601
+#else
602
+        return 0;
603
+#endif
604
+}
605
+
606
+/*
607
+ * This is the tricky part -- do not casually change *anything* in here.  The
608
+ * idea is to build the linked list of entries that are used by fts_children
609
+ * and fts_read.  There are lots of special cases.
610
+ *
611
+ * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is
612
+ * set and it's a physical walk (so that symbolic links can't be directories),
613
+ * we can do things quickly.  First, if it's a 4.4BSD file system, the type
614
+ * of the file is in the directory entry.  Otherwise, we assume that the number
615
+ * of subdirectories in a node is equal to the number of links to the parent.
616
+ * The former skips all stat calls.  The latter skips stat calls in any leaf
617
+ * directories and for any files after the subdirectories in the directory have
618
+ * been found, cutting the stat calls by about 2/3.
619
+ */
620
+static FTSENTRY *
621
+internal_function
622
+fts_build (FTSOBJ *sp, int type)
623
+{
624
+	struct dirent *dp;
625
+	FTSENTRY *p, *head;
626
+	int nitems;
627
+	FTSENTRY *cur, *tail;
628
+	DIR *dirp;
629
+	void *oldaddr;
630
+	int cderrno, descend, len, level, nlinks, saved_errno,
631
+	    nostat, doadjust;
632
+	size_t maxlen;
633
+	char *cp;
634
+
635
+	/* Set current node pointer. */
636
+	cur = sp->fts_cur;
637
+
638
+	/*
639
+	 * Open the directory for reading.  If this fails, we're done.
640
+	 * If being called from fts_read, set the fts_info field.
641
+	 */
642
+#if defined FTS_WHITEOUT && 0
643
+	if (ISSET(FTS_WHITEOUT))
644
+		oflag = DTF_NODUP|DTF_REWIND;
645
+	else
646
+		oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND;
647
+#else
648
+# define __opendir2(path, flag) opendir(path)
649
+#endif
650
+       if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {
651
+		if (type == BREAD) {
652
+			cur->fts_info = FTS_DNR;
653
+			cur->fts_errno = errno;
654
+		}
655
+		return (NULL);
656
+	}
657
+
658
+	/*
659
+	 * Nlinks is the number of possible entries of type directory in the
660
+	 * directory if we're cheating on stat calls, 0 if we're not doing
661
+	 * any stat calls at all, -1 if we're doing stats on everything.
662
+	 */
663
+	if (type == BNAMES) {
664
+		nlinks = 0;
665
+		/* Be quiet about nostat, GCC. */
666
+		nostat = 0;
667
+	} else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
668
+		nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
669
+		nostat = 1;
670
+	} else {
671
+		nlinks = -1;
672
+		nostat = 0;
673
+	}
674
+
675
+#ifdef notdef
676
+	(void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink);
677
+	(void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n",
678
+	    ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));
679
+#endif
680
+	/*
681
+	 * If we're going to need to stat anything or we want to descend
682
+	 * and stay in the directory, chdir.  If this fails we keep going,
683
+	 * but set a flag so we don't chdir after the post-order visit.
684
+	 * We won't be able to stat anything, but we can still return the
685
+	 * names themselves.  Note, that since fts_read won't be able to
686
+	 * chdir into the directory, it will have to return different path
687
+	 * names than before, i.e. "a/b" instead of "b".  Since the node
688
+	 * has already been visited in pre-order, have to wait until the
689
+	 * post-order visit to return the error.  There is a special case
690
+	 * here, if there was nothing to stat then it's not an error to
691
+	 * not be able to stat.  This is all fairly nasty.  If a program
692
+	 * needed sorted entries or stat information, they had better be
693
+	 * checking FTS_NS on the returned nodes.
694
+	 */
695
+	cderrno = 0;
696
+	if (nlinks || type == BREAD) {
697
+		if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) {
698
+			if (nlinks && type == BREAD)
699
+				cur->fts_errno = errno;
700
+			cur->fts_flags |= FTS_DONTCHDIR;
701
+			descend = 0;
702
+			cderrno = errno;
703
+			(void)closedir(dirp);
704
+			dirp = NULL;
705
+		} else
706
+			descend = 1;
707
+	} else
708
+		descend = 0;
709
+
710
+	/*
711
+	 * Figure out the max file name length that can be stored in the
712
+	 * current path -- the inner loop allocates more path as necessary.
713
+	 * We really wouldn't have to do the maxlen calculations here, we
714
+	 * could do them in fts_read before returning the path, but it's a
715
+	 * lot easier here since the length is part of the dirent structure.
716
+	 *
717
+	 * If not changing directories set a pointer so that can just append
718
+	 * each new name into the path.
719
+	 */
720
+	len = NAPPEND(cur);
721
+	if (ISSET(FTS_NOCHDIR)) {
722
+		cp = sp->fts_path + len;
723
+		*cp++ = '/';
724
+	} else {
725
+		/* GCC, you're too verbose. */
726
+		cp = NULL;
727
+	}
728
+	len++;
729
+	maxlen = sp->fts_pathlen - len;
730
+
731
+	level = cur->fts_level + 1;
732
+
733
+	/* Read the directory, attaching each entry to the `link' pointer. */
734
+	doadjust = 0;
735
+	for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) {
736
+		if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
737
+			continue;
738
+
739
+		if ((p = fts_alloc(sp, dp->d_name, _D_EXACT_NAMLEN (dp))) == NULL)
740
+			goto mem1;
741
+		if (_D_EXACT_NAMLEN (dp) >= maxlen) {/* include space for NUL */
742
+			oldaddr = sp->fts_path;
743
+			if (fts_palloc(sp, _D_EXACT_NAMLEN (dp) + len + 1)) {
744
+				/*
745
+				 * No more memory for path or structures.  Save
746
+				 * errno, free up the current structure and the
747
+				 * structures already allocated.
748
+				 */
749
+mem1:				saved_errno = errno;
750
+				free(p);
751
+				fts_lfree(head);
752
+				(void)closedir(dirp);
753
+				cur->fts_info = FTS_ERR;
754
+				SET(FTS_STOP);
755
+				__set_errno (saved_errno);
756
+				return (NULL);
757
+			}
758
+			/* Did realloc() change the pointer? */
759
+			if (oldaddr != sp->fts_path) {
760
+				doadjust = 1;
761
+				if (ISSET(FTS_NOCHDIR))
762
+					cp = sp->fts_path + len;
763
+			}
764
+			maxlen = sp->fts_pathlen - len;
765
+		}
766
+
767
+		if (len + _D_EXACT_NAMLEN (dp) >= USHRT_MAX) {
768
+			/*
769
+			 * In an FTSENT, fts_pathlen is a u_short so it is
770
+			 * possible to wraparound here.  If we do, free up
771
+			 * the current structure and the structures already
772
+			 * allocated, then error out with ENAMETOOLONG.
773
+			 */
774
+			free(p);
775
+			fts_lfree(head);
776
+			(void)closedir(dirp);
777
+			cur->fts_info = FTS_ERR;
778
+			SET(FTS_STOP);
779
+			__set_errno (ENAMETOOLONG);
780
+			return (NULL);
781
+		}
782
+		p->fts_level = level;
783
+		p->fts_parent = sp->fts_cur;
784
+		p->fts_pathlen = len + _D_EXACT_NAMLEN (dp);
785
+
786
+#if defined FTS_WHITEOUT && 0
787
+		if (dp->d_type == DT_WHT)
788
+			p->fts_flags |= FTS_ISW;
789
+#endif
790
+
791
+		/* Unreachable code.  cderrno is only ever set to a nonnull
792
+		   value if dirp is closed at the same time.  But then we
793
+		   cannot enter this loop.  */
794
+		if (0 && cderrno) {
795
+			if (nlinks) {
796
+				p->fts_info = FTS_NS;
797
+				p->fts_errno = cderrno;
798
+			} else
799
+				p->fts_info = FTS_NSOK;
800
+			p->fts_accpath = cur->fts_accpath;
801
+		} else if (nlinks == 0
802
+                           || (nostat && dirent_not_directory(dp))) {
803
+			p->fts_accpath =
804
+			    ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
805
+			p->fts_info = FTS_NSOK;
806
+		} else {
807
+			/* Build a file name for fts_stat to stat. */
808
+			if (ISSET(FTS_NOCHDIR)) {
809
+				p->fts_accpath = p->fts_path;
810
+				memmove(cp, p->fts_name, p->fts_namelen + 1);
811
+			} else
812
+				p->fts_accpath = p->fts_name;
813
+			/* Stat it. */
814
+			p->fts_info = fts_stat(sp, p, 0);
815
+
816
+			/* Decrement link count if applicable. */
817
+			if (nlinks > 0 && (p->fts_info == FTS_D ||
818
+			    p->fts_info == FTS_DC || p->fts_info == FTS_DOT))
819
+				--nlinks;
820
+		}
821
+
822
+		/* We walk in directory order so "ls -f" doesn't get upset. */
823
+		p->fts_link = NULL;
824
+		if (head == NULL)
825
+			head = tail = p;
826
+		else {
827
+			tail->fts_link = p;
828
+			tail = p;
829
+		}
830
+		++nitems;
831
+	}
832
+	if (dirp)
833
+		(void)closedir(dirp);
834
+
835
+	/*
836
+	 * If realloc() changed the address of the path, adjust the
837
+	 * addresses for the rest of the tree and the dir list.
838
+	 */
839
+	if (doadjust)
840
+		fts_padjust(sp, head);
841
+
842
+	/*
843
+	 * If not changing directories, reset the path back to original
844
+	 * state.
845
+	 */
846
+	if (ISSET(FTS_NOCHDIR)) {
847
+		if (len == sp->fts_pathlen || nitems == 0)
848
+			--cp;
849
+		*cp = '\0';
850
+	}
851
+
852
+	/*
853
+	 * If descended after called from fts_children or after called from
854
+	 * fts_read and nothing found, get back.  At the root level we use
855
+	 * the saved fd; if one of fts_open()'s arguments is a relative path
856
+	 * to an empty directory, we wind up here with no other way back.  If
857
+	 * can't get back, we're done.
858
+	 */
859
+	if (descend && (type == BCHILD || !nitems) &&
860
+	    (cur->fts_level == FTS_ROOTLEVEL ?
861
+	     FCHDIR(sp, sp->fts_rfd) :
862
+	     fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
863
+		cur->fts_info = FTS_ERR;
864
+		SET(FTS_STOP);
865
+		fts_lfree(head);
866
+		return (NULL);
867
+	}
868
+
869
+	/* If didn't find anything, return NULL. */
870
+	if (!nitems) {
871
+		if (type == BREAD)
872
+			cur->fts_info = FTS_DP;
873
+		fts_lfree(head);
874
+		return (NULL);
875
+	}
876
+
877
+	/* Sort the entries. */
878
+	if (sp->fts_compar && nitems > 1)
879
+		head = fts_sort(sp, head, nitems);
880
+	return (head);
881
+}
882
+
883
+static u_short
884
+internal_function
885
+fts_stat (FTSOBJ *sp, FTSENTRY *p, int follow)
886
+{
887
+	FTSENTRY *t;
888
+	dev_t dev;
889
+	INO_T ino;
890
+	struct STAT *sbp, sb;
891
+	int saved_errno;
892
+
893
+	/* If user needs stat info, stat buffer already allocated. */
894
+	sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
895
+
896
+#if defined FTS_WHITEOUT && 0
897
+	/* check for whiteout */
898
+	if (p->fts_flags & FTS_ISW) {
899
+		if (sbp != &sb) {
900
+			memset(sbp, '\0', sizeof (*sbp));
901
+			sbp->st_mode = S_IFWHT;
902
+		}
903
+		return (FTS_W);
904
+       }
905
+#endif
906
+
907
+	/*
908
+	 * If doing a logical walk, or application requested FTS_FOLLOW, do
909
+	 * a stat(2).  If that fails, check for a non-existent symlink.  If
910
+	 * fail, set the errno from the stat call.
911
+	 */
912
+	if (ISSET(FTS_LOGICAL) || follow) {
913
+		if (STAT(p->fts_accpath, sbp)) {
914
+			saved_errno = errno;
915
+			if (!LSTAT(p->fts_accpath, sbp)) {
916
+				__set_errno (0);
917
+				return (FTS_SLNONE);
918
+			}
919
+			p->fts_errno = saved_errno;
920
+			goto err;
921
+		}
922
+	} else if (LSTAT(p->fts_accpath, sbp)) {
923
+		p->fts_errno = errno;
924
+err:		memset(sbp, 0, sizeof(struct STAT));
925
+		return (FTS_NS);
926
+	}
927
+
928
+	if (S_ISDIR(sbp->st_mode)) {
929
+		/*
930
+		 * Set the device/inode.  Used to find cycles and check for
931
+		 * crossing mount points.  Also remember the link count, used
932
+		 * in fts_build to limit the number of stat calls.  It is
933
+		 * understood that these fields are only referenced if fts_info
934
+		 * is set to FTS_D.
935
+		 */
936
+		dev = p->fts_dev = sbp->st_dev;
937
+		ino = p->fts_ino = sbp->st_ino;
938
+		p->fts_nlink = sbp->st_nlink;
939
+
940
+		if (ISDOT(p->fts_name))
941
+			return (FTS_DOT);
942
+
943
+		/*
944
+		 * Cycle detection is done by brute force when the directory
945
+		 * is first encountered.  If the tree gets deep enough or the
946
+		 * number of symbolic links to directories is high enough,
947
+		 * something faster might be worthwhile.
948
+		 */
949
+		for (t = p->fts_parent;
950
+		    t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
951
+			if (ino == t->fts_ino && dev == t->fts_dev) {
952
+				p->fts_cycle = t;
953
+				return (FTS_DC);
954
+			}
955
+		return (FTS_D);
956
+	}
957
+	if (S_ISLNK(sbp->st_mode))
958
+		return (FTS_SL);
959
+	if (S_ISREG(sbp->st_mode))
960
+		return (FTS_F);
961
+	return (FTS_DEFAULT);
962
+}
963
+
964
+static FTSENTRY *
965
+internal_function
966
+fts_sort (FTSOBJ *sp, FTSENTRY *head, int nitems)
967
+{
968
+	FTSENTRY **ap, *p;
969
+
970
+	/*
971
+	 * Construct an array of pointers to the structures and call qsort(3).
972
+	 * Reassemble the array in the order returned by qsort.  If unable to
973
+	 * sort for memory reasons, return the directory entries in their
974
+	 * current order.  Allocate enough space for the current needs plus
975
+	 * 40 so don't realloc one entry at a time.
976
+	 */
977
+	if (nitems > sp->fts_nitems) {
978
+		FTSENTRY **a;
979
+
980
+		sp->fts_nitems = nitems + 40;
981
+		if ((a = realloc(sp->fts_array,
982
+		    (size_t)(sp->fts_nitems * sizeof(FTSENTRY *)))) == NULL) {
983
+			free(sp->fts_array);
984
+			sp->fts_array = NULL;
985
+			sp->fts_nitems = 0;
986
+			return (head);
987
+		}
988
+		sp->fts_array = a;
989
+	}
990
+	for (ap = sp->fts_array, p = head; p; p = p->fts_link)
991
+		*ap++ = p;
992
+	qsort((void *)sp->fts_array, nitems, sizeof(FTSENTRY *), sp->fts_compar);
993
+	for (head = *(ap = sp->fts_array); --nitems; ++ap)
994
+		ap[0]->fts_link = ap[1];
995
+	ap[0]->fts_link = NULL;
996
+	return (head);
997
+}
998
+
999
+static FTSENTRY *
1000
+internal_function
1001
+fts_alloc (FTSOBJ *sp, const char *name, size_t namelen)
1002
+{
1003
+	FTSENTRY *p;
1004
+	size_t len;
1005
+
1006
+	/*
1007
+	 * The file name is a variable length array and no stat structure is
1008
+	 * necessary if the user has set the nostat bit.  Allocate the FTSENT
1009
+	 * structure, the file name and the stat structure in one chunk, but
1010
+	 * be careful that the stat structure is reasonably aligned.  Since the
1011
+	 * fts_name field is declared to be of size 1, the fts_name pointer is
1012
+	 * namelen + 2 before the first possible address of the stat structure.
1013
+	 */
1014
+	len = sizeof(FTSENTRY) + namelen;
1015
+	if (!ISSET(FTS_NOSTAT))
1016
+		len += sizeof(struct STAT) + ALIGNBYTES;
1017
+	if ((p = malloc(len)) == NULL)
1018
+		return (NULL);
1019
+
1020
+	/* Copy the name and guarantee NUL termination. */
1021
+	memmove(p->fts_name, name, namelen);
1022
+	p->fts_name[namelen] = '\0';
1023
+
1024
+	if (!ISSET(FTS_NOSTAT))
1025
+		p->fts_statp = (struct STAT *)ALIGN(p->fts_name + namelen + 2);
1026
+	p->fts_namelen = namelen;
1027
+	p->fts_path = sp->fts_path;
1028
+	p->fts_errno = 0;
1029
+	p->fts_flags = 0;
1030
+	p->fts_instr = FTS_NOINSTR;
1031
+	p->fts_number = 0;
1032
+	p->fts_pointer = NULL;
1033
+	return (p);
1034
+}
1035
+
1036
+static void
1037
+internal_function
1038
+fts_lfree (FTSENTRY *head)
1039
+{
1040
+	FTSENTRY *p;
1041
+
1042
+	/* Free a linked list of structures. */
1043
+	while ((p = head)) {
1044
+		head = head->fts_link;
1045
+		free(p);
1046
+	}
1047
+}
1048
+
1049
+/*
1050
+ * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
1051
+ * Most systems will allow creation of paths much longer than MAXPATHLEN, even
1052
+ * though the kernel won't resolve them.  Add the size (not just what's needed)
1053
+ * plus 256 bytes so don't realloc the path 2 bytes at a time.
1054
+ */
1055
+static int
1056
+internal_function
1057
+fts_palloc (FTSOBJ *sp, size_t more)
1058
+{
1059
+	char *p;
1060
+
1061
+	sp->fts_pathlen += more + 256;
1062
+	/*
1063
+	 * Check for possible wraparound.  In an FTS, fts_pathlen is
1064
+	 * a signed int but in an FTSENT it is an unsigned short.
1065
+	 * We limit fts_pathlen to USHRT_MAX to be safe in both cases.
1066
+	 */
1067
+	if (sp->fts_pathlen < 0 || sp->fts_pathlen >= USHRT_MAX) {
1068
+		free(sp->fts_path);
1069
+		sp->fts_path = NULL;
1070
+		__set_errno (ENAMETOOLONG);
1071
+		return (1);
1072
+	}
1073
+	p = realloc(sp->fts_path, sp->fts_pathlen);
1074
+	if (p == NULL) {
1075
+		free(sp->fts_path);
1076
+		sp->fts_path = NULL;
1077
+		return 1;
1078
+	}
1079
+	sp->fts_path = p;
1080
+	return 0;
1081
+}
1082
+
1083
+/*
1084
+ * When the path is realloc'd, have to fix all of the pointers in structures
1085
+ * already returned.
1086
+ */
1087
+static void
1088
+internal_function
1089
+fts_padjust (FTSOBJ *sp, FTSENTRY *head)
1090
+{
1091
+	FTSENTRY *p;
1092
+	char *addr = sp->fts_path;
1093
+
1094
+#define	ADJUST(p) do {							\
1095
+	if ((p)->fts_accpath != (p)->fts_name) {			\
1096
+		(p)->fts_accpath =					\
1097
+		    (char *)addr + ((p)->fts_accpath - (p)->fts_path);	\
1098
+	}								\
1099
+	(p)->fts_path = addr;						\
1100
+} while (0)
1101
+	/* Adjust the current set of children. */
1102
+	for (p = sp->fts_child; p; p = p->fts_link)
1103
+		ADJUST(p);
1104
+
1105
+	/* Adjust the rest of the tree, including the current level. */
1106
+	for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
1107
+		ADJUST(p);
1108
+		p = p->fts_link ? p->fts_link : p->fts_parent;
1109
+	}
1110
+}
1111
+
1112
+static size_t
1113
+internal_function
1114
+fts_maxarglen (char * const *argv)
1115
+{
1116
+	size_t len, max;
1117
+
1118
+	for (max = 0; *argv; ++argv)
1119
+		if ((len = strlen(*argv)) > max)
1120
+			max = len;
1121
+	return (max + 1);
1122
+}
1123
+
1124
+/*
1125
+ * Change to dir specified by fd or p->fts_accpath without getting
1126
+ * tricked by someone changing the world out from underneath us.
1127
+ * Assumes p->fts_dev and p->fts_ino are filled in.
1128
+ */
1129
+static int
1130
+internal_function
1131
+fts_safe_changedir (FTSOBJ *sp, FTSENTRY *p, int fd, const char *path)
1132
+{
1133
+	int ret, oerrno, newfd;
1134
+	struct stat sb;
1135
+
1136
+	newfd = fd;
1137
+	if (ISSET(FTS_NOCHDIR))
1138
+		return (0);
1139
+	if (fd < 0 && (newfd = open(path, O_RDONLY, 0)) < 0)
1140
+		return (-1);
1141
+	if (fstat(newfd, &sb)) {
1142
+		ret = -1;
1143
+		goto bail;
1144
+	}
1145
+	if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) {
1146
+		__set_errno (ENOENT);		/* disinformation */
1147
+		ret = -1;
1148
+		goto bail;
1149
+	}
1150
+	ret = fchdir(newfd);
1151
+bail:
1152
+	oerrno = errno;
1153
+	if (fd < 0)
1154
+		(void)close(newfd);
1155
+	__set_errno (oerrno);
1156
+	return (ret);
1157
+}
... ...
@@ -30,7 +30,6 @@
30 30
 #include <unistd.h>
31 31
 #include <sys/types.h>
32 32
 #include <sys/stat.h>
33
-#include <fts.h>
34 33
 #include <fcntl.h>
35 34
 #include <signal.h>
36 35
 #include <pthread.h>
... ...
@@ -28,7 +28,6 @@
28 28
 #include <unistd.h>
29 29
 #include <sys/types.h>
30 30
 #include <sys/stat.h>
31
-#include <fts.h>
32 31
 #include <fcntl.h>
33 32
 #include <signal.h>
34 33
 #include <pthread.h>
... ...
@@ -51,6 +50,7 @@
51 51
 #include "server.h"
52 52
 #include "others.h"
53 53
 #include "scanner.h"
54
+#include "priv_fts.h"
54 55
 
55 56
 static struct onas_bucket *onas_bucket_init();
56 57
 static void onas_free_bucket(struct onas_bucket *bckt);
... ...
@@ -580,12 +580,12 @@ int onas_ht_add_hierarchy(struct onas_ht *ht, const char *pathname) {
580 580
 	free(prnt);
581 581
 
582 582
 	char * const pathargv[] = { (char*) pathname, NULL };
583
-	if (!(ftsp = fts_open(pathargv, ftspopts, NULL))) {
583
+	if (!(ftsp = _priv_fts_open(pathargv, ftspopts, NULL))) {
584 584
 		logg("!ScanOnAccess: Could not open '%s'\n", pathname);
585 585
 		return CL_EARG;
586 586
 	}
587 587
 
588
-	while((curr = fts_read(ftsp))) {
588
+	while((curr = _priv_fts_read(ftsp))) {
589 589
 
590 590
 		struct onas_hnode *hnode = NULL;
591 591
 
... ...
@@ -608,7 +608,7 @@ int onas_ht_add_hierarchy(struct onas_ht *ht, const char *pathname) {
608 608
 				continue;
609 609
 		}
610 610
 
611
-		if((childlist = fts_children(ftsp, 0))) {
611
+		if((childlist = _priv_fts_children(ftsp, 0))) {
612 612
 			do {
613 613
 				if (childlist->fts_info == FTS_D) {
614 614
 					if(CL_EMEM == onas_add_hashnode_child(hnode, childlist->fts_name))
... ...
@@ -624,7 +624,7 @@ int onas_ht_add_hierarchy(struct onas_ht *ht, const char *pathname) {
624 624
 		if (onas_ht_insert(ht, elem)) return -1;
625 625
 	}
626 626
 
627
-	fts_close(ftsp);
627
+	_priv_fts_close(ftsp);
628 628
 	return CL_SUCCESS;
629 629
 }
630 630
 
... ...
@@ -28,7 +28,6 @@
28 28
 #include <unistd.h>
29 29
 #include <string.h>
30 30
 #include <fcntl.h>
31
-#include <fts.h>
32 31
 #include <signal.h>
33 32
 #include <pthread.h>
34 33
 
... ...
@@ -36,6 +35,7 @@
36 36
 #include "shared/output.h"
37 37
 
38 38
 #include "others.h"
39
+#include "priv_fts.h"
39 40
 
40 41
 #include "onaccess_scth.h"
41 42
 
... ...
@@ -56,10 +56,10 @@ static void onas_scth_handle_dir(const char *pathname) {
56 56
 	FTSENT *curr = NULL;
57 57
 
58 58
 	char *const pathargv[] = { (char *) pathname, NULL };
59
-	if (!(ftsp = fts_open(pathargv, ftspopts, NULL))) return;
59
+	if (!(ftsp = _priv_fts_open(pathargv, ftspopts, NULL))) return;
60 60
 
61 61
 	/* Offload scanning work to fanotify thread to avoid potential deadlocks. */
62
-	while ((curr = fts_read(ftsp))) {
62
+	while ((curr = _priv_fts_read(ftsp))) {
63 63
 		if (curr->fts_info != FTS_D) {
64 64
 			int fd = open(curr->fts_path, O_RDONLY);
65 65
 			if (fd > 0) close(fd);
66 66
new file mode 100644
... ...
@@ -0,0 +1,222 @@
0
+#include "clamav-config.h"
1
+
2
+#if HAVE_SYSTEM_LFS_FTS
3
+#include <fts.h>
4
+
5
+static inline FTSENT *_priv_fts_children(FTS *ftsp, int options)
6
+{
7
+	return fts_children(ftsp, options);
8
+}
9
+
10
+static inline int _priv_fts_close(FTS *ftsp)
11
+{
12
+	return fts_close(ftsp);
13
+}
14
+
15
+static inline FTS *_priv_fts_open (char * const *path_argv, int options,
16
+		   int (*compar)(const FTSENT **, const FTSENT **))
17
+{
18
+	return fts_open(path_argv, options, compar);
19
+}
20
+
21
+static inline FTSENT *_priv_fts_read (FTS *ftsp)
22
+{
23
+	return fts_read(ftsp);
24
+}
25
+
26
+static inline int _priv_fts_set (FTS *ftsp, FTSENT *f, int options)
27
+{
28
+	return fts_set(ftsp, f, options);
29
+}
30
+
31
+#else
32
+/* File tree traversal functions declarations.
33
+   Copyright (C) 1994-2015 Free Software Foundation, Inc.
34
+   This file is part of the GNU C Library.
35
+
36
+   The GNU C Library is free software; you can redistribute it and/or
37
+   modify it under the terms of the GNU Lesser General Public
38
+   License as published by the Free Software Foundation; either
39
+   version 2.1 of the License, or (at your option) any later version.
40
+
41
+   The GNU C Library is distributed in the hope that it will be useful,
42
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
43
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
44
+   Lesser General Public License for more details.
45
+
46
+   You should have received a copy of the GNU Lesser General Public
47
+   License along with the GNU C Library; if not, see
48
+   <http://www.gnu.org/licenses/>.  */
49
+
50
+/*
51
+ * Copyright (c) 1989, 1993
52
+ *	The Regents of the University of California.  All rights reserved.
53
+ *
54
+ * Redistribution and use in source and binary forms, with or without
55
+ * modification, are permitted provided that the following conditions
56
+ * are met:
57
+ * 1. Redistributions of source code must retain the above copyright
58
+ *    notice, this list of conditions and the following disclaimer.
59
+ * 2. Redistributions in binary form must reproduce the above copyright
60
+ *    notice, this list of conditions and the following disclaimer in the
61
+ *    documentation and/or other materials provided with the distribution.
62
+ * 4. Neither the name of the University nor the names of its contributors
63
+ *    may be used to endorse or promote products derived from this software
64
+ *    without specific prior written permission.
65
+ *
66
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
67
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
68
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
69
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
70
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
71
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
72
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
73
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
74
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
75
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
76
+ * SUCH DAMAGE.
77
+ *
78
+ *	@(#)fts.h	8.3 (Berkeley) 8/14/94
79
+ */
80
+
81
+#ifndef	_FTS_H
82
+#define	_FTS_H 1
83
+
84
+#include <features.h>
85
+#include <sys/types.h>
86
+
87
+
88
+typedef struct {
89
+	struct _ftsent *fts_cur;	/* current node */
90
+	struct _ftsent *fts_child;	/* linked list of children */
91
+	struct _ftsent **fts_array;	/* sort array */
92
+	dev_t fts_dev;			/* starting device # */
93
+	char *fts_path;			/* path for this descent */
94
+	int fts_rfd;			/* fd for root */
95
+	int fts_pathlen;		/* sizeof(path) */
96
+	int fts_nitems;			/* elements in the sort array */
97
+	int (*fts_compar) (const void *, const void *); /* compare fn */
98
+
99
+#define	FTS_COMFOLLOW	0x0001		/* follow command line symlinks */
100
+#define	FTS_LOGICAL	0x0002		/* logical walk */
101
+#define	FTS_NOCHDIR	0x0004		/* don't change directories */
102
+#define	FTS_NOSTAT	0x0008		/* don't get stat info */
103
+#define	FTS_PHYSICAL	0x0010		/* physical walk */
104
+#define	FTS_SEEDOT	0x0020		/* return dot and dot-dot */
105
+#define	FTS_XDEV	0x0040		/* don't cross devices */
106
+#define FTS_WHITEOUT	0x0080		/* return whiteout information */
107
+#define	FTS_OPTIONMASK	0x00ff		/* valid user option mask */
108
+
109
+#define	FTS_NAMEONLY	0x0100		/* (private) child names only */
110
+#define	FTS_STOP	0x0200		/* (private) unrecoverable error */
111
+	int fts_options;		/* fts_open options, global flags */
112
+} FTS;
113
+
114
+#ifdef __USE_LARGEFILE64
115
+typedef struct {
116
+	struct _ftsent64 *fts_cur;	/* current node */
117
+	struct _ftsent64 *fts_child;	/* linked list of children */
118
+	struct _ftsent64 **fts_array;	/* sort array */
119
+	dev_t fts_dev;			/* starting device # */
120
+	char *fts_path;			/* path for this descent */
121
+	int fts_rfd;			/* fd for root */
122
+	int fts_pathlen;		/* sizeof(path) */
123
+	int fts_nitems;			/* elements in the sort array */
124
+	int (*fts_compar) (const void *, const void *); /* compare fn */
125
+	int fts_options;		/* fts_open options, global flags */
126
+} FTS64;
127
+#endif
128
+
129
+typedef struct _ftsent {
130
+	struct _ftsent *fts_cycle;	/* cycle node */
131
+	struct _ftsent *fts_parent;	/* parent directory */
132
+	struct _ftsent *fts_link;	/* next file in directory */
133
+	long fts_number;	        /* local numeric value */
134
+	void *fts_pointer;	        /* local address value */
135
+	char *fts_accpath;		/* access path */
136
+	char *fts_path;			/* root path */
137
+	int fts_errno;			/* errno for this node */
138
+	int fts_symfd;			/* fd for symlink */
139
+	u_short fts_pathlen;		/* strlen(fts_path) */
140
+	u_short fts_namelen;		/* strlen(fts_name) */
141
+
142
+	ino_t fts_ino;			/* inode */
143
+	dev_t fts_dev;			/* device */
144
+	nlink_t fts_nlink;		/* link count */
145
+
146
+#define	FTS_ROOTPARENTLEVEL	-1
147
+#define	FTS_ROOTLEVEL		 0
148
+	short fts_level;		/* depth (-1 to N) */
149
+
150
+#define	FTS_D		 1		/* preorder directory */
151
+#define	FTS_DC		 2		/* directory that causes cycles */
152
+#define	FTS_DEFAULT	 3		/* none of the above */
153
+#define	FTS_DNR		 4		/* unreadable directory */
154
+#define	FTS_DOT		 5		/* dot or dot-dot */
155
+#define	FTS_DP		 6		/* postorder directory */
156
+#define	FTS_ERR		 7		/* error; errno is set */
157
+#define	FTS_F		 8		/* regular file */
158
+#define	FTS_INIT	 9		/* initialized only */
159
+#define	FTS_NS		10		/* stat(2) failed */
160
+#define	FTS_NSOK	11		/* no stat(2) requested */
161
+#define	FTS_SL		12		/* symbolic link */
162
+#define	FTS_SLNONE	13		/* symbolic link without target */
163
+#define FTS_W		14		/* whiteout object */
164
+	u_short fts_info;		/* user flags for FTSENT structure */
165
+
166
+#define	FTS_DONTCHDIR	 0x01		/* don't chdir .. to the parent */
167
+#define	FTS_SYMFOLLOW	 0x02		/* followed a symlink to get here */
168
+	u_short fts_flags;		/* private flags for FTSENT structure */
169
+
170
+#define	FTS_AGAIN	 1		/* read node again */
171
+#define	FTS_FOLLOW	 2		/* follow symbolic link */
172
+#define	FTS_NOINSTR	 3		/* no instructions */
173
+#define	FTS_SKIP	 4		/* discard node */
174
+	u_short fts_instr;		/* fts_set() instructions */
175
+
176
+	struct stat *fts_statp;		/* stat(2) information */
177
+	char fts_name[1];		/* file name */
178
+} FTSENT;
179
+
180
+#ifdef __USE_LARGEFILE64
181
+typedef struct _ftsent64 {
182
+	struct _ftsent64 *fts_cycle;	/* cycle node */
183
+	struct _ftsent64 *fts_parent;	/* parent directory */
184
+	struct _ftsent64 *fts_link;	/* next file in directory */
185
+	long fts_number;	        /* local numeric value */
186
+	void *fts_pointer;	        /* local address value */
187
+	char *fts_accpath;		/* access path */
188
+	char *fts_path;			/* root path */
189
+	int fts_errno;			/* errno for this node */
190
+	int fts_symfd;			/* fd for symlink */
191
+	u_short fts_pathlen;		/* strlen(fts_path) */
192
+	u_short fts_namelen;		/* strlen(fts_name) */
193
+
194
+	ino64_t fts_ino;		/* inode */
195
+	dev_t fts_dev;			/* device */
196
+	nlink_t fts_nlink;		/* link count */
197
+
198
+	short fts_level;		/* depth (-1 to N) */
199
+
200
+	u_short fts_info;		/* user flags for FTSENT structure */
201
+
202
+	u_short fts_flags;		/* private flags for FTSENT structure */
203
+
204
+	u_short fts_instr;		/* fts_set() instructions */
205
+
206
+	struct stat64 *fts_statp;	/* stat(2) information */
207
+	char fts_name[1];		/* file name */
208
+} FTSENT64;
209
+#endif
210
+
211
+__BEGIN_DECLS
212
+FTSENT	*_priv_fts_children (FTS *, int);
213
+int	 _priv_fts_close (FTS *);
214
+FTS	*_priv_fts_open (char * const *, int,
215
+		   int (*)(const FTSENT **, const FTSENT **));
216
+FTSENT	*_priv_fts_read (FTS *);
217
+int	 _priv_fts_set (FTS *, FTSENT *, int) __THROW;
218
+__END_DECLS
219
+
220
+#endif /* fts.h */
221
+#endif
... ...
@@ -125,6 +125,7 @@ m4_include([m4/reorganization/distcheck.m4])
125 125
 m4_include([m4/reorganization/llvm.m4])
126 126
 m4_include([m4/reorganization/sha_collect.m4])
127 127
 m4_include([m4/reorganization/yara.m4])
128
+m4_include([m4/reorganization/code_checks/fts.m4])
128 129
 m4_include([m4/reorganization/libfreshclam.m4])
129 130
 
130 131
 m4_include([m4/reorganization/prelude.m4])
... ...
@@ -277,6 +278,8 @@ else
277 277
     CL_MSG_STATUS([libxml2     ],[yes, from $XML_HOME],[])
278 278
 fi
279 279
 CL_MSG_STATUS([yara        ],[$enable_yara],[$enable_yara])
280
+CL_MSG_STATUS([fts         ],[yes],[$lfs_fts_msg])
281
+
280 282
 
281 283
 # Yep, downgrading the compiler avoids the bug too:
282 284
 # 4.0.x, and 4.1.0 are the known buggy versions
283 285
new file mode 100644
... ...
@@ -0,0 +1,16 @@
0
+AC_MSG_CHECKING([LFS safe fts implementation])
1
+AC_TRY_COMPILE([
2
+	        #include <fts.h>
3
+	], [ fts_open((void *)0, FTS_PHYSICAL, (void *)0);],
4
+	[ have_LFS_fts=yes ],
5
+	[ have_LFS_fts=no]
6
+)
7
+AC_MSG_RESULT([$have_LFS_fts])
8
+AM_CONDITIONAL([SYSTEM_LFS_FTS], [test "x$have_LFS_fts" = "xyes"])
9
+if test "x$have_LFS_fts" = "xyes"; then
10
+	AC_DEFINE([HAVE_SYSTEM_LFS_FTS], [1], [Use libc's fts() implementation])
11
+	lfs_fts_msg="libc"
12
+else
13
+	AC_DEFINE([HAVE_SYSTEM_LFS_FTS], [0], [Use private fts() implementation which is LFS safe])
14
+	lfs_fts_msg="internal, libc's is not LFS compatible"
15
+fi