| GCC Code Coverage Report | |||||||||||||||||||||
        
  | 
    |||||||||||||||||||||
| Line | Branch | Exec | Source | 
1  | 
    /* $OpenBSD: fts.c,v 1.58 2017/03/17 15:14:40 deraadt Exp $ */  | 
    ||
2  | 
    |||
3  | 
    /*-  | 
    ||
4  | 
    * Copyright (c) 1990, 1993, 1994  | 
    ||
5  | 
    * The Regents of the University of California. All rights reserved.  | 
    ||
6  | 
    *  | 
    ||
7  | 
    * Redistribution and use in source and binary forms, with or without  | 
    ||
8  | 
    * modification, are permitted provided that the following conditions  | 
    ||
9  | 
    * are met:  | 
    ||
10  | 
    * 1. Redistributions of source code must retain the above copyright  | 
    ||
11  | 
    * notice, this list of conditions and the following disclaimer.  | 
    ||
12  | 
    * 2. Redistributions in binary form must reproduce the above copyright  | 
    ||
13  | 
    * notice, this list of conditions and the following disclaimer in the  | 
    ||
14  | 
    * documentation and/or other materials provided with the distribution.  | 
    ||
15  | 
    * 3. Neither the name of the University nor the names of its contributors  | 
    ||
16  | 
    * may be used to endorse or promote products derived from this software  | 
    ||
17  | 
    * without specific prior written permission.  | 
    ||
18  | 
    *  | 
    ||
19  | 
    * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND  | 
    ||
20  | 
    * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  | 
    ||
21  | 
    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  | 
    ||
22  | 
    * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE  | 
    ||
23  | 
    * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  | 
    ||
24  | 
    * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS  | 
    ||
25  | 
    * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)  | 
    ||
26  | 
    * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT  | 
    ||
27  | 
    * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY  | 
    ||
28  | 
    * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  | 
    ||
29  | 
    * SUCH DAMAGE.  | 
    ||
30  | 
    */  | 
    ||
31  | 
    |||
32  | 
    #include <sys/param.h> /* ALIGN */  | 
    ||
33  | 
    #include <sys/stat.h>  | 
    ||
34  | 
    |||
35  | 
    #include <dirent.h>  | 
    ||
36  | 
    #include <errno.h>  | 
    ||
37  | 
    #include <fcntl.h>  | 
    ||
38  | 
    #include <fts.h>  | 
    ||
39  | 
    #include <limits.h>  | 
    ||
40  | 
    #include <stdlib.h>  | 
    ||
41  | 
    #include <string.h>  | 
    ||
42  | 
    #include <unistd.h>  | 
    ||
43  | 
    |||
44  | 
    #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))  | 
    ||
45  | 
    |||
46  | 
    static FTSENT *fts_alloc(FTS *, char *, size_t);  | 
    ||
47  | 
    static FTSENT *fts_build(FTS *, int);  | 
    ||
48  | 
    static void fts_lfree(FTSENT *);  | 
    ||
49  | 
    static void fts_load(FTS *, FTSENT *);  | 
    ||
50  | 
    static size_t fts_maxarglen(char * const *);  | 
    ||
51  | 
    static void fts_padjust(FTS *, FTSENT *);  | 
    ||
52  | 
    static int fts_palloc(FTS *, size_t);  | 
    ||
53  | 
    static FTSENT *fts_sort(FTS *, FTSENT *, int);  | 
    ||
54  | 
    static u_short fts_stat(FTS *, FTSENT *, int, int);  | 
    ||
55  | 
    static int fts_safe_changedir(FTS *, FTSENT *, int, char *);  | 
    ||
56  | 
    |||
57  | 
    #define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))  | 
    ||
58  | 
    |||
59  | 
    #define CLR(opt) (sp->fts_options &= ~(opt))  | 
    ||
60  | 
    #define ISSET(opt) (sp->fts_options & (opt))  | 
    ||
61  | 
    #define SET(opt) (sp->fts_options |= (opt))  | 
    ||
62  | 
    |||
63  | 
    #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd))  | 
    ||
64  | 
    |||
65  | 
    /* fts_build flags */  | 
    ||
66  | 
    #define BCHILD 1 /* fts_children */  | 
    ||
67  | 
    #define BNAMES 2 /* fts_children, names only */  | 
    ||
68  | 
    #define BREAD 3 /* fts_read */  | 
    ||
69  | 
    |||
70  | 
    FTS *  | 
    ||
71  | 
    fts_open(char * const *argv, int options,  | 
    ||
72  | 
    int (*compar)(const FTSENT **, const FTSENT **))  | 
    ||
73  | 
    { | 
    ||
74  | 
    FTS *sp;  | 
    ||
75  | 
    FTSENT *p, *root;  | 
    ||
76  | 
    int nitems;  | 
    ||
77  | 
    FTSENT *parent, *prev;  | 
    ||
78  | 
    |||
79  | 
    /* Options check. */  | 
    ||
80  | 
    ✗✓ | 12258  | 
    	if (options & ~FTS_OPTIONMASK) { | 
    
81  | 
    errno = EINVAL;  | 
    ||
82  | 
    return (NULL);  | 
    ||
83  | 
    }  | 
    ||
84  | 
    |||
85  | 
    /* At least one path must be specified. */  | 
    ||
86  | 
    ✗✓ | 6129  | 
    	if (*argv == NULL) { | 
    
87  | 
    errno = EINVAL;  | 
    ||
88  | 
    return (NULL);  | 
    ||
89  | 
    }  | 
    ||
90  | 
    |||
91  | 
    /* Allocate/initialize the stream */  | 
    ||
92  | 
    ✗✓ | 6129  | 
    if ((sp = calloc(1, sizeof(FTS))) == NULL)  | 
    
93  | 
    return (NULL);  | 
    ||
94  | 
    6129  | 
    sp->fts_compar = compar;  | 
    |
95  | 
    6129  | 
    sp->fts_options = options;  | 
    |
96  | 
    |||
97  | 
    /* Logical walks turn on NOCHDIR; symbolic links are too hard. */  | 
    ||
98  | 
    ✓✓ | 6129  | 
    if (ISSET(FTS_LOGICAL))  | 
    
99  | 
    418  | 
    SET(FTS_NOCHDIR);  | 
    |
100  | 
    |||
101  | 
    /*  | 
    ||
102  | 
    * Start out with 1K of path space, and enough, in any case,  | 
    ||
103  | 
    * to hold the user's paths.  | 
    ||
104  | 
    */  | 
    ||
105  | 
    ✗✓✓✗ | 
    12258  | 
    if (fts_palloc(sp, MAXIMUM(fts_maxarglen(argv), PATH_MAX)))  | 
    
106  | 
    goto mem1;  | 
    ||
107  | 
    |||
108  | 
    /* Allocate/initialize root's parent. */  | 
    ||
109  | 
    ✓✗ | 6129  | 
    if ((parent = fts_alloc(sp, "", 0)) == NULL)  | 
    
110  | 
    goto mem2;  | 
    ||
111  | 
    6129  | 
    parent->fts_level = FTS_ROOTPARENTLEVEL;  | 
    |
112  | 
    |||
113  | 
    /* Allocate/initialize root(s). */  | 
    ||
114  | 
    ✓✓ | 2321116  | 
    	for (root = prev = NULL, nitems = 0; *argv; ++argv, ++nitems) { | 
    
115  | 
    ✓✗ | 1154429  | 
    if ((p = fts_alloc(sp, *argv, strlen(*argv))) == NULL)  | 
    
116  | 
    goto mem3;  | 
    ||
117  | 
    1154429  | 
    p->fts_level = FTS_ROOTLEVEL;  | 
    |
118  | 
    1154429  | 
    p->fts_parent = parent;  | 
    |
119  | 
    1154429  | 
    p->fts_accpath = p->fts_name;  | 
    |
120  | 
    1154429  | 
    p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), -1);  | 
    |
121  | 
    |||
122  | 
    /* Command-line "." and ".." are real directories. */  | 
    ||
123  | 
    ✓✓ | 1154429  | 
    if (p->fts_info == FTS_DOT)  | 
    
124  | 
    5  | 
    p->fts_info = FTS_D;  | 
    |
125  | 
    |||
126  | 
    /*  | 
    ||
127  | 
    * If comparison routine supplied, traverse in sorted  | 
    ||
128  | 
    * order; otherwise traverse in the order specified.  | 
    ||
129  | 
    */  | 
    ||
130  | 
    ✓✓ | 1154429  | 
    		if (compar) { | 
    
131  | 
    1147640  | 
    p->fts_link = root;  | 
    |
132  | 
    root = p;  | 
    ||
133  | 
    1147640  | 
    		} else { | 
    |
134  | 
    6789  | 
    p->fts_link = NULL;  | 
    |
135  | 
    ✓✓ | 6789  | 
    if (root == NULL)  | 
    
136  | 
    4077  | 
    root = p;  | 
    |
137  | 
    else  | 
    ||
138  | 
    2712  | 
    prev->fts_link = p;  | 
    |
139  | 
    prev = p;  | 
    ||
140  | 
    }  | 
    ||
141  | 
    }  | 
    ||
142  | 
    ✓✓ | 6129  | 
    if (compar && nitems > 1)  | 
    
143  | 
    2032  | 
    root = fts_sort(sp, root, nitems);  | 
    |
144  | 
    |||
145  | 
    /*  | 
    ||
146  | 
    * Allocate a dummy pointer and make fts_read think that we've just  | 
    ||
147  | 
    * finished the node before the root(s); set p->fts_info to FTS_INIT  | 
    ||
148  | 
    * so that everything about the "current" node is ignored.  | 
    ||
149  | 
    */  | 
    ||
150  | 
    ✓✗ | 6129  | 
    if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)  | 
    
151  | 
    goto mem3;  | 
    ||
152  | 
    6129  | 
    sp->fts_cur->fts_link = root;  | 
    |
153  | 
    6129  | 
    sp->fts_cur->fts_info = FTS_INIT;  | 
    |
154  | 
    |||
155  | 
    /*  | 
    ||
156  | 
    * If using chdir(2), grab a file descriptor pointing to dot to ensure  | 
    ||
157  | 
    * that we can get back here; this could be avoided for some paths,  | 
    ||
158  | 
    * but almost certainly not worth the effort. Slashes, symbolic links,  | 
    ||
159  | 
    * and ".." are all fairly nasty problems. Note, if we can't get the  | 
    ||
160  | 
    * descriptor we run anyway, just more slowly.  | 
    ||
161  | 
    */  | 
    ||
162  | 
    ✓✓✗✓ | 
    11728  | 
    if (!ISSET(FTS_NOCHDIR) &&  | 
    
163  | 
    5599  | 
    	    (sp->fts_rfd = open(".", O_RDONLY | O_CLOEXEC)) < 0) | 
    |
164  | 
    SET(FTS_NOCHDIR);  | 
    ||
165  | 
    |||
166  | 
    ✗✓ | 6129  | 
    if (nitems == 0)  | 
    
167  | 
    free(parent);  | 
    ||
168  | 
    |||
169  | 
    6129  | 
    return (sp);  | 
    |
170  | 
    |||
171  | 
    mem3: fts_lfree(root);  | 
    ||
172  | 
    free(parent);  | 
    ||
173  | 
    mem2: free(sp->fts_path);  | 
    ||
174  | 
    mem1: free(sp);  | 
    ||
175  | 
    return (NULL);  | 
    ||
176  | 
    6129  | 
    }  | 
    |
177  | 
    DEF_WEAK(fts_open);  | 
    ||
178  | 
    |||
179  | 
    static void  | 
    ||
180  | 
    fts_load(FTS *sp, FTSENT *p)  | 
    ||
181  | 
    { | 
    ||
182  | 
    size_t len;  | 
    ||
183  | 
    char *cp;  | 
    ||
184  | 
    |||
185  | 
    /*  | 
    ||
186  | 
    * Load the stream structure for the next traversal. Since we don't  | 
    ||
187  | 
    * actually enter the directory until after the preorder visit, set  | 
    ||
188  | 
    * the fts_accpath field specially so the chdir gets done to the right  | 
    ||
189  | 
    * place and the user can access the first node. From fts_open it's  | 
    ||
190  | 
    * known that the path will fit.  | 
    ||
191  | 
    */  | 
    ||
192  | 
    2308858  | 
    len = p->fts_pathlen = p->fts_namelen;  | 
    |
193  | 
    1154429  | 
    memmove(sp->fts_path, p->fts_name, len + 1);  | 
    |
194  | 
    ✓✓✗✓ ✗✗  | 
    2231948  | 
    	if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { | 
    
195  | 
    1077519  | 
    len = strlen(++cp);  | 
    |
196  | 
    1077519  | 
    memmove(p->fts_name, cp, len + 1);  | 
    |
197  | 
    1077519  | 
    p->fts_namelen = len;  | 
    |
198  | 
    1077519  | 
    }  | 
    |
199  | 
    1154429  | 
    p->fts_accpath = p->fts_path = sp->fts_path;  | 
    |
200  | 
    1154429  | 
    sp->fts_dev = p->fts_dev;  | 
    |
201  | 
    1154429  | 
    }  | 
    |
202  | 
    |||
203  | 
    int  | 
    ||
204  | 
    fts_close(FTS *sp)  | 
    ||
205  | 
    { | 
    ||
206  | 
    FTSENT *freep, *p;  | 
    ||
207  | 
    int rfd, error = 0;  | 
    ||
208  | 
    |||
209  | 
    /*  | 
    ||
210  | 
    * This still works if we haven't read anything -- the dummy structure  | 
    ||
211  | 
    * points to the root list, so we step through to the end of the root  | 
    ||
212  | 
    * list which has a valid parent pointer.  | 
    ||
213  | 
    */  | 
    ||
214  | 
    ✗✓ | 12258  | 
    	if (sp->fts_cur) { | 
    
215  | 
    		for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { | 
    ||
216  | 
    freep = p;  | 
    ||
217  | 
    p = p->fts_link ? p->fts_link : p->fts_parent;  | 
    ||
218  | 
    free(freep);  | 
    ||
219  | 
    }  | 
    ||
220  | 
    free(p);  | 
    ||
221  | 
    }  | 
    ||
222  | 
    |||
223  | 
    /* Stash the original directory fd if needed. */  | 
    ||
224  | 
    ✓✓ | 17857  | 
    rfd = ISSET(FTS_NOCHDIR) ? -1 : sp->fts_rfd;  | 
    
225  | 
    |||
226  | 
    /* Free up child linked list, sort array, path buffer, stream ptr.*/  | 
    ||
227  | 
    ✗✓ | 6129  | 
    if (sp->fts_child)  | 
    
228  | 
    fts_lfree(sp->fts_child);  | 
    ||
229  | 
    6129  | 
    free(sp->fts_array);  | 
    |
230  | 
    6129  | 
    free(sp->fts_path);  | 
    |
231  | 
    6129  | 
    free(sp);  | 
    |
232  | 
    |||
233  | 
    /* Return to original directory, checking for error. */  | 
    ||
234  | 
    ✓✓ | 6129  | 
    	if (rfd != -1) { | 
    
235  | 
    int saved_errno;  | 
    ||
236  | 
    5599  | 
    error = fchdir(rfd);  | 
    |
237  | 
    5599  | 
    saved_errno = errno;  | 
    |
238  | 
    5599  | 
    (void)close(rfd);  | 
    |
239  | 
    5599  | 
    errno = saved_errno;  | 
    |
240  | 
    5599  | 
    }  | 
    |
241  | 
    |||
242  | 
    6129  | 
    return (error);  | 
    |
243  | 
    }  | 
    ||
244  | 
    DEF_WEAK(fts_close);  | 
    ||
245  | 
    |||
246  | 
    /*  | 
    ||
247  | 
    * Special case of "/" at the end of the path so that slashes aren't  | 
    ||
248  | 
    * appended which would cause paths to be written as "....//foo".  | 
    ||
249  | 
    */  | 
    ||
250  | 
    #define NAPPEND(p) \  | 
    ||
251  | 
    (p->fts_path[p->fts_pathlen - 1] == '/' \  | 
    ||
252  | 
    ? p->fts_pathlen - 1 : p->fts_pathlen)  | 
    ||
253  | 
    |||
254  | 
    FTSENT *  | 
    ||
255  | 
    fts_read(FTS *sp)  | 
    ||
256  | 
    { | 
    ||
257  | 
    FTSENT *p, *tmp;  | 
    ||
258  | 
    int instr;  | 
    ||
259  | 
    char *t;  | 
    ||
260  | 
    int saved_errno;  | 
    ||
261  | 
    |||
262  | 
    /* If finished or unrecoverable error, return NULL. */  | 
    ||
263  | 
    ✓✗✗✓ | 
    3518202  | 
    if (sp->fts_cur == NULL || ISSET(FTS_STOP))  | 
    
264  | 
    return (NULL);  | 
    ||
265  | 
    |||
266  | 
    /* Set current node pointer. */  | 
    ||
267  | 
    p = sp->fts_cur;  | 
    ||
268  | 
    |||
269  | 
    /* Save and zero out user instructions. */  | 
    ||
270  | 
    1172734  | 
    instr = p->fts_instr;  | 
    |
271  | 
    1172734  | 
    p->fts_instr = FTS_NOINSTR;  | 
    |
272  | 
    |||
273  | 
    /* Any type of file may be re-visited; re-stat and re-turn. */  | 
    ||
274  | 
    ✗✓ | 1172734  | 
    	if (instr == FTS_AGAIN) { | 
    
275  | 
    p->fts_info = fts_stat(sp, p, 0, -1);  | 
    ||
276  | 
    return (p);  | 
    ||
277  | 
    }  | 
    ||
278  | 
    |||
279  | 
    /*  | 
    ||
280  | 
    * Following a symlink -- SLNONE test allows application to see  | 
    ||
281  | 
    * SLNONE and recover. If indirecting through a symlink, have  | 
    ||
282  | 
    * keep a pointer to current location. If unable to get that  | 
    ||
283  | 
    * pointer, follow fails.  | 
    ||
284  | 
    */  | 
    ||
285  | 
    ✗✓✗✗ | 
    1172734  | 
    if (instr == FTS_FOLLOW &&  | 
    
286  | 
    	    (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { | 
    ||
287  | 
    p->fts_info = fts_stat(sp, p, 1, -1);  | 
    ||
288  | 
    		if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { | 
    ||
289  | 
    if ((p->fts_symfd =  | 
    ||
290  | 
    			    open(".", O_RDONLY | O_CLOEXEC)) < 0) { | 
    ||
291  | 
    p->fts_errno = errno;  | 
    ||
292  | 
    p->fts_info = FTS_ERR;  | 
    ||
293  | 
    } else  | 
    ||
294  | 
    p->fts_flags |= FTS_SYMFOLLOW;  | 
    ||
295  | 
    }  | 
    ||
296  | 
    return (p);  | 
    ||
297  | 
    }  | 
    ||
298  | 
    |||
299  | 
    /* Directory in pre-order. */  | 
    ||
300  | 
    ✓✓ | 1172734  | 
    	if (p->fts_info == FTS_D) { | 
    
301  | 
    /* If skipped or crossed mount point, do post-order visit. */  | 
    ||
302  | 
    ✓✓✗✗ | 
    2678  | 
    if (instr == FTS_SKIP ||  | 
    
303  | 
    ✗✓ | 2439  | 
    		    (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) { | 
    
304  | 
    ✗✓ | 239  | 
    if (p->fts_flags & FTS_SYMFOLLOW)  | 
    
305  | 
    (void)close(p->fts_symfd);  | 
    ||
306  | 
    ✓✓ | 239  | 
    			if (sp->fts_child) { | 
    
307  | 
    6  | 
    fts_lfree(sp->fts_child);  | 
    |
308  | 
    6  | 
    sp->fts_child = NULL;  | 
    |
309  | 
    6  | 
    }  | 
    |
310  | 
    239  | 
    p->fts_info = FTS_DP;  | 
    |
311  | 
    239  | 
    return (p);  | 
    |
312  | 
    }  | 
    ||
313  | 
    |||
314  | 
    /* Rebuild if only read the names and now traversing. */  | 
    ||
315  | 
    ✗✓✗✗ | 
    2439  | 
    		if (sp->fts_child && ISSET(FTS_NAMEONLY)) { | 
    
316  | 
    CLR(FTS_NAMEONLY);  | 
    ||
317  | 
    fts_lfree(sp->fts_child);  | 
    ||
318  | 
    sp->fts_child = NULL;  | 
    ||
319  | 
    }  | 
    ||
320  | 
    |||
321  | 
    /*  | 
    ||
322  | 
    * Cd to the subdirectory.  | 
    ||
323  | 
    *  | 
    ||
324  | 
    * If have already read and now fail to chdir, whack the list  | 
    ||
325  | 
    * to make the names come out right, and set the parent errno  | 
    ||
326  | 
    * so the application will eventually get an error condition.  | 
    ||
327  | 
    * Set the FTS_DONTCHDIR flag so that when we logically change  | 
    ||
328  | 
    * directories back to the parent we don't do a chdir.  | 
    ||
329  | 
    *  | 
    ||
330  | 
    * If haven't read do so. If the read fails, fts_build sets  | 
    ||
331  | 
    * FTS_STOP or the fts_info field of the node.  | 
    ||
332  | 
    */  | 
    ||
333  | 
    ✗✓ | 2439  | 
    		if (sp->fts_child) { | 
    
334  | 
    			if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { | 
    ||
335  | 
    p->fts_errno = errno;  | 
    ||
336  | 
    p->fts_flags |= FTS_DONTCHDIR;  | 
    ||
337  | 
    for (p = sp->fts_child; p; p = p->fts_link)  | 
    ||
338  | 
    p->fts_accpath =  | 
    ||
339  | 
    p->fts_parent->fts_accpath;  | 
    ||
340  | 
    }  | 
    ||
341  | 
    ✓✓ | 2439  | 
    		} else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { | 
    
342  | 
    ✗✓ | 33  | 
    if (ISSET(FTS_STOP))  | 
    
343  | 
    return (NULL);  | 
    ||
344  | 
    33  | 
    return (p);  | 
    |
345  | 
    }  | 
    ||
346  | 
    2406  | 
    p = sp->fts_child;  | 
    |
347  | 
    2406  | 
    sp->fts_child = NULL;  | 
    |
348  | 
    2406  | 
    goto name;  | 
    |
349  | 
    }  | 
    ||
350  | 
    |||
351  | 
    /* Move to the next node on this level. */  | 
    ||
352  | 
    next: tmp = p;  | 
    ||
353  | 
    ✓✓ | 1170056  | 
    	if ((p = p->fts_link)) { | 
    
354  | 
    1161521  | 
    free(tmp);  | 
    |
355  | 
    |||
356  | 
    /*  | 
    ||
357  | 
    * If reached the top, return to the original directory (or  | 
    ||
358  | 
    * the root of the tree), and load the paths for the next root.  | 
    ||
359  | 
    */  | 
    ||
360  | 
    ✓✓ | 1161521  | 
    		if (p->fts_level == FTS_ROOTLEVEL) { | 
    
361  | 
    ✓✓✗✓ | 
    2308328  | 
    			if (FCHDIR(sp, sp->fts_rfd)) { | 
    
362  | 
    SET(FTS_STOP);  | 
    ||
363  | 
    return (NULL);  | 
    ||
364  | 
    }  | 
    ||
365  | 
    1154429  | 
    fts_load(sp, p);  | 
    |
366  | 
    1154429  | 
    return (sp->fts_cur = p);  | 
    |
367  | 
    }  | 
    ||
368  | 
    |||
369  | 
    /*  | 
    ||
370  | 
    * User may have called fts_set on the node. If skipped,  | 
    ||
371  | 
    * ignore. If followed, get a file descriptor so we can  | 
    ||
372  | 
    * get back if necessary.  | 
    ||
373  | 
    */  | 
    ||
374  | 
    ✗✓ | 7092  | 
    if (p->fts_instr == FTS_SKIP)  | 
    
375  | 
    goto next;  | 
    ||
376  | 
    ✗✓ | 7092  | 
    		if (p->fts_instr == FTS_FOLLOW) { | 
    
377  | 
    p->fts_info = fts_stat(sp, p, 1, -1);  | 
    ||
378  | 
    			if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { | 
    ||
379  | 
    if ((p->fts_symfd =  | 
    ||
380  | 
    				    open(".", O_RDONLY | O_CLOEXEC)) < 0) { | 
    ||
381  | 
    p->fts_errno = errno;  | 
    ||
382  | 
    p->fts_info = FTS_ERR;  | 
    ||
383  | 
    } else  | 
    ||
384  | 
    p->fts_flags |= FTS_SYMFOLLOW;  | 
    ||
385  | 
    }  | 
    ||
386  | 
    p->fts_instr = FTS_NOINSTR;  | 
    ||
387  | 
    }  | 
    ||
388  | 
    |||
389  | 
    9498  | 
    name: t = sp->fts_path + NAPPEND(p->fts_parent);  | 
    |
390  | 
    9498  | 
    *t++ = '/';  | 
    |
391  | 
    9498  | 
    memmove(t, p->fts_name, p->fts_namelen + 1);  | 
    |
392  | 
    9498  | 
    return (sp->fts_cur = p);  | 
    |
393  | 
    }  | 
    ||
394  | 
    |||
395  | 
    /* Move up to the parent node. */  | 
    ||
396  | 
    8535  | 
    p = tmp->fts_parent;  | 
    |
397  | 
    8535  | 
    free(tmp);  | 
    |
398  | 
    |||
399  | 
    ✓✓ | 8535  | 
    	if (p->fts_level == FTS_ROOTPARENTLEVEL) { | 
    
400  | 
    /*  | 
    ||
401  | 
    * Done; free everything up and set errno to 0 so the user  | 
    ||
402  | 
    * can distinguish between error and EOF.  | 
    ||
403  | 
    */  | 
    ||
404  | 
    6129  | 
    free(p);  | 
    |
405  | 
    6129  | 
    errno = 0;  | 
    |
406  | 
    6129  | 
    return (sp->fts_cur = NULL);  | 
    |
407  | 
    }  | 
    ||
408  | 
    |||
409  | 
    /* NUL terminate the pathname. */  | 
    ||
410  | 
    2406  | 
    sp->fts_path[p->fts_pathlen] = '\0';  | 
    |
411  | 
    |||
412  | 
    /*  | 
    ||
413  | 
    * Return to the parent directory. If at a root node or came through  | 
    ||
414  | 
    * a symlink, go back through the file descriptor. Otherwise, cd up  | 
    ||
415  | 
    * one directory.  | 
    ||
416  | 
    */  | 
    ||
417  | 
    ✓✓ | 2406  | 
    	if (p->fts_level == FTS_ROOTLEVEL) { | 
    
418  | 
    ✓✓✗✓ | 
    2829  | 
    		if (FCHDIR(sp, sp->fts_rfd)) { | 
    
419  | 
    SET(FTS_STOP);  | 
    ||
420  | 
    sp->fts_cur = p;  | 
    ||
421  | 
    return (NULL);  | 
    ||
422  | 
    }  | 
    ||
423  | 
    ✗✓ | 895  | 
    	} else if (p->fts_flags & FTS_SYMFOLLOW) { | 
    
424  | 
    		if (FCHDIR(sp, p->fts_symfd)) { | 
    ||
425  | 
    saved_errno = errno;  | 
    ||
426  | 
    (void)close(p->fts_symfd);  | 
    ||
427  | 
    errno = saved_errno;  | 
    ||
428  | 
    SET(FTS_STOP);  | 
    ||
429  | 
    sp->fts_cur = p;  | 
    ||
430  | 
    return (NULL);  | 
    ||
431  | 
    }  | 
    ||
432  | 
    (void)close(p->fts_symfd);  | 
    ||
433  | 
    ✓✗✗✓ | 
    1790  | 
    } else if (!(p->fts_flags & FTS_DONTCHDIR) &&  | 
    
434  | 
    895  | 
    	    fts_safe_changedir(sp, p->fts_parent, -1, "..")) { | 
    |
435  | 
    SET(FTS_STOP);  | 
    ||
436  | 
    sp->fts_cur = p;  | 
    ||
437  | 
    return (NULL);  | 
    ||
438  | 
    }  | 
    ||
439  | 
    2406  | 
    p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;  | 
    |
440  | 
    2406  | 
    return (sp->fts_cur = p);  | 
    |
441  | 
    1172734  | 
    }  | 
    |
442  | 
    DEF_WEAK(fts_read);  | 
    ||
443  | 
    |||
444  | 
    /*  | 
    ||
445  | 
    * Fts_set takes the stream as an argument although it's not used in this  | 
    ||
446  | 
    * implementation; it would be necessary if anyone wanted to add global  | 
    ||
447  | 
    * semantics to fts using fts_set. An error return is allowed for similar  | 
    ||
448  | 
    * reasons.  | 
    ||
449  | 
    */  | 
    ||
450  | 
    int  | 
    ||
451  | 
    fts_set(FTS *sp, FTSENT *p, int instr)  | 
    ||
452  | 
    { | 
    ||
453  | 
    ✗✓ | 956  | 
    if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW &&  | 
    
454  | 
    478  | 
    	    instr != FTS_NOINSTR && instr != FTS_SKIP) { | 
    |
455  | 
    errno = EINVAL;  | 
    ||
456  | 
    return (1);  | 
    ||
457  | 
    }  | 
    ||
458  | 
    239  | 
    p->fts_instr = instr;  | 
    |
459  | 
    239  | 
    return (0);  | 
    |
460  | 
    239  | 
    }  | 
    |
461  | 
    DEF_WEAK(fts_set);  | 
    ||
462  | 
    |||
463  | 
    FTSENT *  | 
    ||
464  | 
    fts_children(FTS *sp, int instr)  | 
    ||
465  | 
    { | 
    ||
466  | 
    FTSENT *p;  | 
    ||
467  | 
    int fd;  | 
    ||
468  | 
    |||
469  | 
    ✗✓ | 4116  | 
    	if (instr && instr != FTS_NAMEONLY) { | 
    
470  | 
    errno = EINVAL;  | 
    ||
471  | 
    return (NULL);  | 
    ||
472  | 
    }  | 
    ||
473  | 
    |||
474  | 
    /* Set current node pointer. */  | 
    ||
475  | 
    2058  | 
    p = sp->fts_cur;  | 
    |
476  | 
    |||
477  | 
    /*  | 
    ||
478  | 
    * Errno set to 0 so user can distinguish empty directory from  | 
    ||
479  | 
    * an error.  | 
    ||
480  | 
    */  | 
    ||
481  | 
    2058  | 
    errno = 0;  | 
    |
482  | 
    |||
483  | 
    /* Fatal errors stop here. */  | 
    ||
484  | 
    ✗✓ | 2058  | 
    if (ISSET(FTS_STOP))  | 
    
485  | 
    return (NULL);  | 
    ||
486  | 
    |||
487  | 
    /* Return logical hierarchy of user's arguments. */  | 
    ||
488  | 
    ✓✓ | 2058  | 
    if (p->fts_info == FTS_INIT)  | 
    
489  | 
    2052  | 
    return (p->fts_link);  | 
    |
490  | 
    |||
491  | 
    /*  | 
    ||
492  | 
    * If not a directory being visited in pre-order, stop here. Could  | 
    ||
493  | 
    * allow FTS_DNR, assuming the user has fixed the problem, but the  | 
    ||
494  | 
    * same effect is available with FTS_AGAIN.  | 
    ||
495  | 
    */  | 
    ||
496  | 
    ✗✓ | 6  | 
    if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)  | 
    
497  | 
    return (NULL);  | 
    ||
498  | 
    |||
499  | 
    /* Free up any previous child list. */  | 
    ||
500  | 
    ✗✓ | 6  | 
    if (sp->fts_child)  | 
    
501  | 
    fts_lfree(sp->fts_child);  | 
    ||
502  | 
    |||
503  | 
    ✓✓ | 6  | 
    	if (instr == FTS_NAMEONLY) { | 
    
504  | 
    4  | 
    SET(FTS_NAMEONLY);  | 
    |
505  | 
    instr = BNAMES;  | 
    ||
506  | 
    4  | 
    } else  | 
    |
507  | 
    instr = BCHILD;  | 
    ||
508  | 
    |||
509  | 
    /*  | 
    ||
510  | 
    * If using chdir on a relative path and called BEFORE fts_read does  | 
    ||
511  | 
    * its chdir to the root of a traversal, we can lose -- we need to  | 
    ||
512  | 
    * chdir into the subdirectory, and we don't know where the current  | 
    ||
513  | 
    * directory is, so we can't get back so that the upcoming chdir by  | 
    ||
514  | 
    * fts_read will work.  | 
    ||
515  | 
    */  | 
    ||
516  | 
    ✓✗✓✗ ✗✓  | 
    18  | 
    if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||  | 
    
517  | 
    6  | 
    ISSET(FTS_NOCHDIR))  | 
    |
518  | 
    return (sp->fts_child = fts_build(sp, instr));  | 
    ||
519  | 
    |||
520  | 
    ✗✓ | 6  | 
    	if ((fd = open(".", O_RDONLY | O_CLOEXEC)) < 0) | 
    
521  | 
    return (NULL);  | 
    ||
522  | 
    6  | 
    sp->fts_child = fts_build(sp, instr);  | 
    |
523  | 
    ✗✓ | 12  | 
    	if (fchdir(fd)) { | 
    
524  | 
    6  | 
    (void)close(fd);  | 
    |
525  | 
    return (NULL);  | 
    ||
526  | 
    }  | 
    ||
527  | 
    (void)close(fd);  | 
    ||
528  | 
    6  | 
    return (sp->fts_child);  | 
    |
529  | 
    2058  | 
    }  | 
    |
530  | 
    DEF_WEAK(fts_children);  | 
    ||
531  | 
    |||
532  | 
    /*  | 
    ||
533  | 
    * This is the tricky part -- do not casually change *anything* in here. The  | 
    ||
534  | 
    * idea is to build the linked list of entries that are used by fts_children  | 
    ||
535  | 
    * and fts_read. There are lots of special cases.  | 
    ||
536  | 
    *  | 
    ||
537  | 
    * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is  | 
    ||
538  | 
    * set and it's a physical walk (so that symbolic links can't be directories),  | 
    ||
539  | 
    * we can do things quickly. First, if it's a 4.4BSD file system, the type  | 
    ||
540  | 
    * of the file is in the directory entry. Otherwise, we assume that the number  | 
    ||
541  | 
    * of subdirectories in a node is equal to the number of links to the parent.  | 
    ||
542  | 
    * The former skips all stat calls. The latter skips stat calls in any leaf  | 
    ||
543  | 
    * directories and for any files after the subdirectories in the directory have  | 
    ||
544  | 
    * been found, cutting the stat calls by about 2/3.  | 
    ||
545  | 
    */  | 
    ||
546  | 
    static FTSENT *  | 
    ||
547  | 
    fts_build(FTS *sp, int type)  | 
    ||
548  | 
    { | 
    ||
549  | 
    struct dirent *dp;  | 
    ||
550  | 
    FTSENT *p, *head;  | 
    ||
551  | 
    FTSENT *cur, *tail;  | 
    ||
552  | 
    DIR *dirp;  | 
    ||
553  | 
    void *oldaddr;  | 
    ||
554  | 
    size_t len, maxlen;  | 
    ||
555  | 
    int nitems, cderrno, descend, level, nlinks, nostat, doadjust;  | 
    ||
556  | 
    int saved_errno;  | 
    ||
557  | 
    char *cp;  | 
    ||
558  | 
    |||
559  | 
    /* Set current node pointer. */  | 
    ||
560  | 
    4890  | 
    cur = sp->fts_cur;  | 
    |
561  | 
    |||
562  | 
    /*  | 
    ||
563  | 
    * Open the directory for reading. If this fails, we're done.  | 
    ||
564  | 
    * If being called from fts_read, set the fts_info field.  | 
    ||
565  | 
    */  | 
    ||
566  | 
    ✗✓ | 2445  | 
    	if ((dirp = opendir(cur->fts_accpath)) == NULL) { | 
    
567  | 
    		if (type == BREAD) { | 
    ||
568  | 
    cur->fts_info = FTS_DNR;  | 
    ||
569  | 
    cur->fts_errno = errno;  | 
    ||
570  | 
    }  | 
    ||
571  | 
    return (NULL);  | 
    ||
572  | 
    }  | 
    ||
573  | 
    |||
574  | 
    /*  | 
    ||
575  | 
    * Nlinks is the number of possible entries of type directory in the  | 
    ||
576  | 
    * directory if we're cheating on stat calls, 0 if we're not doing  | 
    ||
577  | 
    * any stat calls at all, -1 if we're doing stats on everything.  | 
    ||
578  | 
    */  | 
    ||
579  | 
    ✓✓ | 2445  | 
    if (type == BNAMES)  | 
    
580  | 
    4  | 
    nlinks = 0;  | 
    |
581  | 
    ✓✓✓✗ | 
    3307  | 
    	else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) { | 
    
582  | 
    866  | 
    nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);  | 
    |
583  | 
    nostat = 1;  | 
    ||
584  | 
    866  | 
    	} else { | 
    |
585  | 
    nlinks = -1;  | 
    ||
586  | 
    nostat = 0;  | 
    ||
587  | 
    }  | 
    ||
588  | 
    |||
589  | 
    #ifdef notdef  | 
    ||
590  | 
    	(void)printf("nlinks == %d (cur: %u)\n", nlinks, cur->fts_nlink); | 
    ||
591  | 
    	(void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", | 
    ||
592  | 
    ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));  | 
    ||
593  | 
    #endif  | 
    ||
594  | 
    /*  | 
    ||
595  | 
    * If we're going to need to stat anything or we want to descend  | 
    ||
596  | 
    * and stay in the directory, chdir. If this fails we keep going,  | 
    ||
597  | 
    * but set a flag so we don't chdir after the post-order visit.  | 
    ||
598  | 
    * We won't be able to stat anything, but we can still return the  | 
    ||
599  | 
    * names themselves. Note, that since fts_read won't be able to  | 
    ||
600  | 
    * chdir into the directory, it will have to return different path  | 
    ||
601  | 
    * names than before, i.e. "a/b" instead of "b". Since the node  | 
    ||
602  | 
    * has already been visited in pre-order, have to wait until the  | 
    ||
603  | 
    * post-order visit to return the error. There is a special case  | 
    ||
604  | 
    * here, if there was nothing to stat then it's not an error to  | 
    ||
605  | 
    * not be able to stat. This is all fairly nasty. If a program  | 
    ||
606  | 
    * needed sorted entries or stat information, they had better be  | 
    ||
607  | 
    * checking FTS_NS on the returned nodes.  | 
    ||
608  | 
    */  | 
    ||
609  | 
    cderrno = 0;  | 
    ||
610  | 
    ✓✓ | 2445  | 
    	if (nlinks || type == BREAD) { | 
    
611  | 
    ✗✓ | 2441  | 
    		if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) { | 
    
612  | 
    if (nlinks && type == BREAD)  | 
    ||
613  | 
    cur->fts_errno = errno;  | 
    ||
614  | 
    cur->fts_flags |= FTS_DONTCHDIR;  | 
    ||
615  | 
    descend = 0;  | 
    ||
616  | 
    cderrno = errno;  | 
    ||
617  | 
    (void)closedir(dirp);  | 
    ||
618  | 
    dirp = NULL;  | 
    ||
619  | 
    } else  | 
    ||
620  | 
    descend = 1;  | 
    ||
621  | 
    } else  | 
    ||
622  | 
    descend = 0;  | 
    ||
623  | 
    |||
624  | 
    /*  | 
    ||
625  | 
    * Figure out the max file name length that can be stored in the  | 
    ||
626  | 
    * current path -- the inner loop allocates more path as necessary.  | 
    ||
627  | 
    * We really wouldn't have to do the maxlen calculations here, we  | 
    ||
628  | 
    * could do them in fts_read before returning the path, but it's a  | 
    ||
629  | 
    * lot easier here since the length is part of the dirent structure.  | 
    ||
630  | 
    *  | 
    ||
631  | 
    * If not changing directories set a pointer so that can just append  | 
    ||
632  | 
    * each new name into the path.  | 
    ||
633  | 
    */  | 
    ||
634  | 
    2445  | 
    len = NAPPEND(cur);  | 
    |
635  | 
    ✓✓ | 2445  | 
    	if (ISSET(FTS_NOCHDIR)) { | 
    
636  | 
    477  | 
    cp = sp->fts_path + len;  | 
    |
637  | 
    477  | 
    *cp++ = '/';  | 
    |
638  | 
    477  | 
    }  | 
    |
639  | 
    2445  | 
    len++;  | 
    |
640  | 
    2445  | 
    maxlen = sp->fts_pathlen - len;  | 
    |
641  | 
    |||
642  | 
    /*  | 
    ||
643  | 
    * fts_level is signed so we must prevent it from wrapping  | 
    ||
644  | 
    * around to FTS_ROOTLEVEL and FTS_ROOTPARENTLEVEL.  | 
    ||
645  | 
    */  | 
    ||
646  | 
    2445  | 
    level = cur->fts_level;  | 
    |
647  | 
    ✓✗ | 2445  | 
    if (level < FTS_MAXLEVEL)  | 
    
648  | 
    2445  | 
    level++;  | 
    |
649  | 
    |||
650  | 
    /* Read the directory, attaching each entry to the `link' pointer. */  | 
    ||
651  | 
    doadjust = 0;  | 
    ||
652  | 
    ✓✗✓✓ | 
    60097  | 
    	for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) { | 
    
653  | 
    ✓✓✓✓ ✓✓✓✓ ✓✗  | 
    43447  | 
    if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))  | 
    
654  | 
    continue;  | 
    ||
655  | 
    |||
656  | 
    ✓✗ | 14330  | 
    if (!(p = fts_alloc(sp, dp->d_name, dp->d_namlen)))  | 
    
657  | 
    goto mem1;  | 
    ||
658  | 
    ✗✓ | 14330  | 
    		if (dp->d_namlen >= maxlen) {	/* include space for NUL */ | 
    
659  | 
    oldaddr = sp->fts_path;  | 
    ||
660  | 
    			if (fts_palloc(sp, dp->d_namlen +len + 1)) { | 
    ||
661  | 
    /*  | 
    ||
662  | 
    * No more memory for path or structures. Save  | 
    ||
663  | 
    * errno, free up the current structure and the  | 
    ||
664  | 
    * structures already allocated.  | 
    ||
665  | 
    */  | 
    ||
666  | 
    mem1: saved_errno = errno;  | 
    ||
667  | 
    free(p);  | 
    ||
668  | 
    fts_lfree(head);  | 
    ||
669  | 
    (void)closedir(dirp);  | 
    ||
670  | 
    cur->fts_info = FTS_ERR;  | 
    ||
671  | 
    SET(FTS_STOP);  | 
    ||
672  | 
    errno = saved_errno;  | 
    ||
673  | 
    return (NULL);  | 
    ||
674  | 
    }  | 
    ||
675  | 
    /* Did realloc() change the pointer? */  | 
    ||
676  | 
    			if (oldaddr != sp->fts_path) { | 
    ||
677  | 
    doadjust = 1;  | 
    ||
678  | 
    if (ISSET(FTS_NOCHDIR))  | 
    ||
679  | 
    cp = sp->fts_path + len;  | 
    ||
680  | 
    }  | 
    ||
681  | 
    maxlen = sp->fts_pathlen - len;  | 
    ||
682  | 
    }  | 
    ||
683  | 
    |||
684  | 
    14330  | 
    p->fts_level = level;  | 
    |
685  | 
    14330  | 
    p->fts_parent = sp->fts_cur;  | 
    |
686  | 
    14330  | 
    p->fts_pathlen = len + dp->d_namlen;  | 
    |
687  | 
    ✗✓ | 14330  | 
    		if (p->fts_pathlen < len) { | 
    
688  | 
    /*  | 
    ||
689  | 
    * If we wrap, free up the current structure and  | 
    ||
690  | 
    * the structures already allocated, then error  | 
    ||
691  | 
    * out with ENAMETOOLONG.  | 
    ||
692  | 
    */  | 
    ||
693  | 
    free(p);  | 
    ||
694  | 
    fts_lfree(head);  | 
    ||
695  | 
    (void)closedir(dirp);  | 
    ||
696  | 
    cur->fts_info = FTS_ERR;  | 
    ||
697  | 
    SET(FTS_STOP);  | 
    ||
698  | 
    errno = ENAMETOOLONG;  | 
    ||
699  | 
    return (NULL);  | 
    ||
700  | 
    }  | 
    ||
701  | 
    |||
702  | 
    ✗✓ | 14330  | 
    		if (cderrno) { | 
    
703  | 
    			if (nlinks) { | 
    ||
704  | 
    p->fts_info = FTS_NS;  | 
    ||
705  | 
    p->fts_errno = cderrno;  | 
    ||
706  | 
    } else  | 
    ||
707  | 
    p->fts_info = FTS_NSOK;  | 
    ||
708  | 
    p->fts_accpath = cur->fts_accpath;  | 
    ||
709  | 
    ✓✗ | 14332  | 
    } else if (nlinks == 0  | 
    
710  | 
    #ifdef DT_DIR  | 
    ||
711  | 
    ✓✓✓✓ | 
    25296  | 
    || (nostat &&  | 
    
712  | 
    ✓✓ | 293  | 
    dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)  | 
    
713  | 
    #endif  | 
    ||
714  | 
    		    ) { | 
    ||
715  | 
    3366  | 
    p->fts_accpath =  | 
    |
716  | 
    ✗✓ | 10098  | 
    ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;  | 
    
717  | 
    3366  | 
    p->fts_info = FTS_NSOK;  | 
    |
718  | 
    3366  | 
    		} else { | 
    |
719  | 
    /* Build a file name for fts_stat to stat. */  | 
    ||
720  | 
    ✓✓ | 10964  | 
    			if (ISSET(FTS_NOCHDIR)) { | 
    
721  | 
    1347  | 
    p->fts_accpath = p->fts_path;  | 
    |
722  | 
    1347  | 
    memmove(cp, p->fts_name, p->fts_namelen + 1);  | 
    |
723  | 
    1347  | 
    p->fts_info = fts_stat(sp, p, 0, dirfd(dirp));  | 
    |
724  | 
    1347  | 
    			} else { | 
    |
725  | 
    9617  | 
    p->fts_accpath = p->fts_name;  | 
    |
726  | 
    9617  | 
    p->fts_info = fts_stat(sp, p, 0, -1);  | 
    |
727  | 
    }  | 
    ||
728  | 
    |||
729  | 
    /* Decrement link count if applicable. */  | 
    ||
730  | 
    ✓✓✗✓ ✗✗  | 
    11253  | 
    if (nlinks > 0 && (p->fts_info == FTS_D ||  | 
    
731  | 
    p->fts_info == FTS_DC || p->fts_info == FTS_DOT))  | 
    ||
732  | 
    289  | 
    --nlinks;  | 
    |
733  | 
    }  | 
    ||
734  | 
    |||
735  | 
    /* We walk in directory order so "ls -f" doesn't get upset. */  | 
    ||
736  | 
    14330  | 
    p->fts_link = NULL;  | 
    |
737  | 
    ✓✓ | 14330  | 
    if (head == NULL)  | 
    
738  | 
    2412  | 
    head = tail = p;  | 
    |
739  | 
    		else { | 
    ||
740  | 
    11918  | 
    tail->fts_link = p;  | 
    |
741  | 
    tail = p;  | 
    ||
742  | 
    }  | 
    ||
743  | 
    14330  | 
    ++nitems;  | 
    |
744  | 
    }  | 
    ||
745  | 
    ✓✗ | 2445  | 
    if (dirp)  | 
    
746  | 
    2445  | 
    (void)closedir(dirp);  | 
    |
747  | 
    |||
748  | 
    /*  | 
    ||
749  | 
    * If realloc() changed the address of the path, adjust the  | 
    ||
750  | 
    * addresses for the rest of the tree and the dir list.  | 
    ||
751  | 
    */  | 
    ||
752  | 
    ✗✓ | 2445  | 
    if (doadjust)  | 
    
753  | 
    fts_padjust(sp, head);  | 
    ||
754  | 
    |||
755  | 
    /*  | 
    ||
756  | 
    * If not changing directories, reset the path back to original  | 
    ||
757  | 
    * state.  | 
    ||
758  | 
    */  | 
    ||
759  | 
    ✓✓ | 2445  | 
    	if (ISSET(FTS_NOCHDIR)) { | 
    
760  | 
    ✓✓ | 477  | 
    if (len == sp->fts_pathlen || nitems == 0)  | 
    
761  | 
    10  | 
    --cp;  | 
    |
762  | 
    477  | 
    *cp = '\0';  | 
    |
763  | 
    477  | 
    }  | 
    |
764  | 
    |||
765  | 
    /*  | 
    ||
766  | 
    * If descended after called from fts_children or after called from  | 
    ||
767  | 
    * fts_read and nothing found, get back. At the root level we use  | 
    ||
768  | 
    * the saved fd; if one of fts_open()'s arguments is a relative path  | 
    ||
769  | 
    * to an empty directory, we wind up here with no other way back. If  | 
    ||
770  | 
    * can't get back, we're done.  | 
    ||
771  | 
    */  | 
    ||
772  | 
    ✓✓✓✓ ✓✓✗✓  | 
    4935  | 
    if (descend && (type == BCHILD || !nitems) &&  | 
    
773  | 
    ✓✓✗✓ | 
    67  | 
    (cur->fts_level == FTS_ROOTLEVEL ? FCHDIR(sp, sp->fts_rfd) :  | 
    
774  | 
    14  | 
    	    fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { | 
    |
775  | 
    cur->fts_info = FTS_ERR;  | 
    ||
776  | 
    SET(FTS_STOP);  | 
    ||
777  | 
    return (NULL);  | 
    ||
778  | 
    }  | 
    ||
779  | 
    |||
780  | 
    /* If didn't find anything, return NULL. */  | 
    ||
781  | 
    ✓✓ | 2445  | 
    	if (!nitems) { | 
    
782  | 
    ✓✗ | 33  | 
    if (type == BREAD)  | 
    
783  | 
    33  | 
    cur->fts_info = FTS_DP;  | 
    |
784  | 
    33  | 
    return (NULL);  | 
    |
785  | 
    }  | 
    ||
786  | 
    |||
787  | 
    /* Sort the entries. */  | 
    ||
788  | 
    ✓✓ | 2412  | 
    if (sp->fts_compar && nitems > 1)  | 
    
789  | 
    6  | 
    head = fts_sort(sp, head, nitems);  | 
    |
790  | 
    2412  | 
    return (head);  | 
    |
791  | 
    2445  | 
    }  | 
    |
792  | 
    |||
793  | 
    static u_short  | 
    ||
794  | 
    fts_stat(FTS *sp, FTSENT *p, int follow, int dfd)  | 
    ||
795  | 
    { | 
    ||
796  | 
    FTSENT *t;  | 
    ||
797  | 
    dev_t dev;  | 
    ||
798  | 
    ino_t ino;  | 
    ||
799  | 
    2330786  | 
    struct stat *sbp, sb;  | 
    |
800  | 
    int saved_errno;  | 
    ||
801  | 
    const char *path;  | 
    ||
802  | 
    |||
803  | 
    ✓✓ | 1165393  | 
    	if (dfd == -1) { | 
    
804  | 
    1164046  | 
    path = p->fts_accpath;  | 
    |
805  | 
    dfd = AT_FDCWD;  | 
    ||
806  | 
    1164046  | 
    } else  | 
    |
807  | 
    1347  | 
    path = p->fts_name;  | 
    |
808  | 
    |||
809  | 
    /* If user needs stat info, stat buffer already allocated. */  | 
    ||
810  | 
    ✓✓ | 2346826  | 
    sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;  | 
    
811  | 
    |||
812  | 
    /*  | 
    ||
813  | 
    * If doing a logical walk, or application requested FTS_FOLLOW, do  | 
    ||
814  | 
    * a stat(2). If that fails, check for a non-existent symlink. If  | 
    ||
815  | 
    * fail, set the errno from the stat call.  | 
    ||
816  | 
    */  | 
    ||
817  | 
    ✓✓ | 1165393  | 
    	if (ISSET(FTS_LOGICAL) || follow) { | 
    
818  | 
    ✗✓ | 1152999  | 
    		if (fstatat(dfd, path, sbp, 0)) { | 
    
819  | 
    saved_errno = errno;  | 
    ||
820  | 
    			if (!fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) { | 
    ||
821  | 
    errno = 0;  | 
    ||
822  | 
    return (FTS_SLNONE);  | 
    ||
823  | 
    }  | 
    ||
824  | 
    p->fts_errno = saved_errno;  | 
    ||
825  | 
    goto err;  | 
    ||
826  | 
    }  | 
    ||
827  | 
    ✓✓ | 12394  | 
    	} else if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) { | 
    
828  | 
    457  | 
    p->fts_errno = errno;  | 
    |
829  | 
    457  | 
    err: memset(sbp, 0, sizeof(struct stat));  | 
    |
830  | 
    457  | 
    return (FTS_NS);  | 
    |
831  | 
    }  | 
    ||
832  | 
    |||
833  | 
    ✓✓ | 1164936  | 
    	if (S_ISDIR(sbp->st_mode)) { | 
    
834  | 
    /*  | 
    ||
835  | 
    * Set the device/inode. Used to find cycles and check for  | 
    ||
836  | 
    * crossing mount points. Also remember the link count, used  | 
    ||
837  | 
    * in fts_build to limit the number of stat calls. It is  | 
    ||
838  | 
    * understood that these fields are only referenced if fts_info  | 
    ||
839  | 
    * is set to FTS_D.  | 
    ||
840  | 
    */  | 
    ||
841  | 
    2714  | 
    dev = p->fts_dev = sbp->st_dev;  | 
    |
842  | 
    2714  | 
    ino = p->fts_ino = sbp->st_ino;  | 
    |
843  | 
    2714  | 
    p->fts_nlink = sbp->st_nlink;  | 
    |
844  | 
    |||
845  | 
    ✓✓✓✓ ✓✗✓✗  | 
    2727  | 
    if (ISDOT(p->fts_name))  | 
    
846  | 
    9  | 
    return (FTS_DOT);  | 
    |
847  | 
    |||
848  | 
    /*  | 
    ||
849  | 
    * Cycle detection is done by brute force when the directory  | 
    ||
850  | 
    * is first encountered. If the tree gets deep enough or the  | 
    ||
851  | 
    * number of symbolic links to directories is high enough,  | 
    ||
852  | 
    * something faster might be worthwhile.  | 
    ||
853  | 
    */  | 
    ||
854  | 
    ✓✓ | 7488  | 
    for (t = p->fts_parent;  | 
    
855  | 
    4783  | 
    t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)  | 
    |
856  | 
    ✗✓✗✗ | 
    1039  | 
    			if (ino == t->fts_ino && dev == t->fts_dev) { | 
    
857  | 
    p->fts_cycle = t;  | 
    ||
858  | 
    return (FTS_DC);  | 
    ||
859  | 
    }  | 
    ||
860  | 
    2705  | 
    return (FTS_D);  | 
    |
861  | 
    }  | 
    ||
862  | 
    ✓✓ | 1162222  | 
    if (S_ISLNK(sbp->st_mode))  | 
    
863  | 
    1265  | 
    return (FTS_SL);  | 
    |
864  | 
    ✓✗ | 1160957  | 
    if (S_ISREG(sbp->st_mode))  | 
    
865  | 
    1160957  | 
    return (FTS_F);  | 
    |
866  | 
    return (FTS_DEFAULT);  | 
    ||
867  | 
    1165393  | 
    }  | 
    |
868  | 
    |||
869  | 
    static FTSENT *  | 
    ||
870  | 
    fts_sort(FTS *sp, FTSENT *head, int nitems)  | 
    ||
871  | 
    { | 
    ||
872  | 
    FTSENT **ap, *p;  | 
    ||
873  | 
    |||
874  | 
    /*  | 
    ||
875  | 
    * Construct an array of pointers to the structures and call qsort(3).  | 
    ||
876  | 
    * Reassemble the array in the order returned by qsort. If unable to  | 
    ||
877  | 
    * sort for memory reasons, return the directory entries in their  | 
    ||
878  | 
    * current order. Allocate enough space for the current needs plus  | 
    ||
879  | 
    * 40 so don't realloc one entry at a time.  | 
    ||
880  | 
    */  | 
    ||
881  | 
    ✓✗ | 4076  | 
    	if (nitems > sp->fts_nitems) { | 
    
882  | 
    struct _ftsent **a;  | 
    ||
883  | 
    |||
884  | 
    ✗✓ | 6114  | 
    if ((a = reallocarray(sp->fts_array,  | 
    
885  | 
    4076  | 
    		    nitems + 40, sizeof(FTSENT *))) == NULL) { | 
    |
886  | 
    free(sp->fts_array);  | 
    ||
887  | 
    sp->fts_array = NULL;  | 
    ||
888  | 
    sp->fts_nitems = 0;  | 
    ||
889  | 
    return (head);  | 
    ||
890  | 
    }  | 
    ||
891  | 
    2038  | 
    sp->fts_nitems = nitems + 40;  | 
    |
892  | 
    2038  | 
    sp->fts_array = a;  | 
    |
893  | 
    ✓✗ | 2038  | 
    }  | 
    
894  | 
    ✓✓ | 2308980  | 
    for (ap = sp->fts_array, p = head; p; p = p->fts_link)  | 
    
895  | 
    1152452  | 
    *ap++ = p;  | 
    |
896  | 
    2038  | 
    qsort(sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar);  | 
    |
897  | 
    ✓✓ | 2304904  | 
    for (head = *(ap = sp->fts_array); --nitems; ++ap)  | 
    
898  | 
    1150414  | 
    ap[0]->fts_link = ap[1];  | 
    |
899  | 
    2038  | 
    ap[0]->fts_link = NULL;  | 
    |
900  | 
    2038  | 
    return (head);  | 
    |
901  | 
    2038  | 
    }  | 
    |
902  | 
    |||
903  | 
    static FTSENT *  | 
    ||
904  | 
    fts_alloc(FTS *sp, char *name, size_t namelen)  | 
    ||
905  | 
    { | 
    ||
906  | 
    FTSENT *p;  | 
    ||
907  | 
    size_t len;  | 
    ||
908  | 
    |||
909  | 
    /*  | 
    ||
910  | 
    * The file name is a variable length array and no stat structure is  | 
    ||
911  | 
    * necessary if the user has set the nostat bit. Allocate the FTSENT  | 
    ||
912  | 
    * structure, the file name and the stat structure in one chunk, but  | 
    ||
913  | 
    * be careful that the stat structure is reasonably aligned. Since the  | 
    ||
914  | 
    * fts_name field is declared to be of size 1, the fts_name pointer is  | 
    ||
915  | 
    * namelen + 2 before the first possible address of the stat structure.  | 
    ||
916  | 
    */  | 
    ||
917  | 
    2362034  | 
    len = sizeof(FTSENT) + namelen;  | 
    |
918  | 
    ✓✓ | 1181017  | 
    if (!ISSET(FTS_NOSTAT))  | 
    
919  | 
    22888  | 
    len += sizeof(struct stat) + ALIGNBYTES;  | 
    |
920  | 
    ✗✓ | 1181017  | 
    if ((p = calloc(1, len)) == NULL)  | 
    
921  | 
    return (NULL);  | 
    ||
922  | 
    |||
923  | 
    1181017  | 
    p->fts_path = sp->fts_path;  | 
    |
924  | 
    1181017  | 
    p->fts_namelen = namelen;  | 
    |
925  | 
    1181017  | 
    p->fts_instr = FTS_NOINSTR;  | 
    |
926  | 
    ✓✓ | 1181017  | 
    if (!ISSET(FTS_NOSTAT))  | 
    
927  | 
    22888  | 
    p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2);  | 
    |
928  | 
    1181017  | 
    memcpy(p->fts_name, name, namelen);  | 
    |
929  | 
    |||
930  | 
    1181017  | 
    return (p);  | 
    |
931  | 
    1181017  | 
    }  | 
    |
932  | 
    |||
933  | 
    static void  | 
    ||
934  | 
    fts_lfree(FTSENT *head)  | 
    ||
935  | 
    { | 
    ||
936  | 
    FTSENT *p;  | 
    ||
937  | 
    |||
938  | 
    /* Free a linked list of structures. */  | 
    ||
939  | 
    ✓✓ | 9682  | 
    	while ((p = head)) { | 
    
940  | 
    4832  | 
    head = head->fts_link;  | 
    |
941  | 
    4832  | 
    free(p);  | 
    |
942  | 
    }  | 
    ||
943  | 
    6  | 
    }  | 
    |
944  | 
    |||
945  | 
    /*  | 
    ||
946  | 
    * Allow essentially unlimited paths; find, rm, ls should all work on any tree.  | 
    ||
947  | 
    * Most systems will allow creation of paths much longer than PATH_MAX, even  | 
    ||
948  | 
    * though the kernel won't resolve them. Add the size (not just what's needed)  | 
    ||
949  | 
    * plus 256 bytes so don't realloc the path 2 bytes at a time.  | 
    ||
950  | 
    */  | 
    ||
951  | 
    static int  | 
    ||
952  | 
    fts_palloc(FTS *sp, size_t more)  | 
    ||
953  | 
    { | 
    ||
954  | 
    char *p;  | 
    ||
955  | 
    |||
956  | 
    /*  | 
    ||
957  | 
    * Check for possible wraparound.  | 
    ||
958  | 
    */  | 
    ||
959  | 
    12258  | 
    more += 256;  | 
    |
960  | 
    ✗✓ | 6129  | 
    	if (sp->fts_pathlen + more < sp->fts_pathlen) { | 
    
961  | 
    free(sp->fts_path);  | 
    ||
962  | 
    sp->fts_path = NULL;  | 
    ||
963  | 
    errno = ENAMETOOLONG;  | 
    ||
964  | 
    return (1);  | 
    ||
965  | 
    }  | 
    ||
966  | 
    6129  | 
    p = recallocarray(sp->fts_path, sp->fts_pathlen,  | 
    |
967  | 
    sp->fts_pathlen + more, 1);  | 
    ||
968  | 
    ✗✓ | 6129  | 
    	if (p == NULL) { | 
    
969  | 
    free(sp->fts_path);  | 
    ||
970  | 
    sp->fts_path = NULL;  | 
    ||
971  | 
    return (1);  | 
    ||
972  | 
    }  | 
    ||
973  | 
    6129  | 
    sp->fts_pathlen += more;  | 
    |
974  | 
    6129  | 
    sp->fts_path = p;  | 
    |
975  | 
    6129  | 
    return (0);  | 
    |
976  | 
    6129  | 
    }  | 
    |
977  | 
    |||
978  | 
    /*  | 
    ||
979  | 
    * When the path is realloc'd, have to fix all of the pointers in structures  | 
    ||
980  | 
    * already returned.  | 
    ||
981  | 
    */  | 
    ||
982  | 
    static void  | 
    ||
983  | 
    fts_padjust(FTS *sp, FTSENT *head)  | 
    ||
984  | 
    { | 
    ||
985  | 
    FTSENT *p;  | 
    ||
986  | 
    char *addr = sp->fts_path;  | 
    ||
987  | 
    |||
988  | 
    #define	ADJUST(p) {							\ | 
    ||
989  | 
    	if ((p)->fts_accpath != (p)->fts_name) {			\ | 
    ||
990  | 
    (p)->fts_accpath = \  | 
    ||
991  | 
    (char *)addr + ((p)->fts_accpath - (p)->fts_path); \  | 
    ||
992  | 
    } \  | 
    ||
993  | 
    (p)->fts_path = addr; \  | 
    ||
994  | 
    }  | 
    ||
995  | 
    /* Adjust the current set of children. */  | 
    ||
996  | 
    for (p = sp->fts_child; p; p = p->fts_link)  | 
    ||
997  | 
    ADJUST(p);  | 
    ||
998  | 
    |||
999  | 
    /* Adjust the rest of the tree, including the current level. */  | 
    ||
1000  | 
    	for (p = head; p->fts_level >= FTS_ROOTLEVEL;) { | 
    ||
1001  | 
    ADJUST(p);  | 
    ||
1002  | 
    p = p->fts_link ? p->fts_link : p->fts_parent;  | 
    ||
1003  | 
    }  | 
    ||
1004  | 
    }  | 
    ||
1005  | 
    |||
1006  | 
    static size_t  | 
    ||
1007  | 
    fts_maxarglen(char * const *argv)  | 
    ||
1008  | 
    { | 
    ||
1009  | 
    size_t len, max;  | 
    ||
1010  | 
    |||
1011  | 
    ✓✓ | 2327245  | 
    for (max = 0; *argv; ++argv)  | 
    
1012  | 
    ✓✓ | 1154429  | 
    if ((len = strlen(*argv)) > max)  | 
    
1013  | 
    8396  | 
    max = len;  | 
    |
1014  | 
    6129  | 
    return (max + 1);  | 
    |
1015  | 
    }  | 
    ||
1016  | 
    |||
1017  | 
    /*  | 
    ||
1018  | 
    * Change to dir specified by fd or p->fts_accpath without getting  | 
    ||
1019  | 
    * tricked by someone changing the world out from underneath us.  | 
    ||
1020  | 
    * Assumes p->fts_dev and p->fts_ino are filled in.  | 
    ||
1021  | 
    */  | 
    ||
1022  | 
    static int  | 
    ||
1023  | 
    fts_safe_changedir(FTS *sp, FTSENT *p, int fd, char *path)  | 
    ||
1024  | 
    { | 
    ||
1025  | 
    int ret, oerrno, newfd;  | 
    ||
1026  | 
    6700  | 
    struct stat sb;  | 
    |
1027  | 
    |||
1028  | 
    newfd = fd;  | 
    ||
1029  | 
    ✓✓ | 3350  | 
    if (ISSET(FTS_NOCHDIR))  | 
    
1030  | 
    751  | 
    return (0);  | 
    |
1031  | 
    ✓✓✗✓ | 
    3234  | 
    if (fd < 0 && (newfd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC)) < 0)  | 
    
1032  | 
    return (-1);  | 
    ||
1033  | 
    ✗✓ | 2599  | 
    	if (fstat(newfd, &sb)) { | 
    
1034  | 
    ret = -1;  | 
    ||
1035  | 
    goto bail;  | 
    ||
1036  | 
    }  | 
    ||
1037  | 
    ✓✗✗✓ | 
    5198  | 
    	if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) { | 
    
1038  | 
    errno = ENOENT; /* disinformation */  | 
    ||
1039  | 
    ret = -1;  | 
    ||
1040  | 
    goto bail;  | 
    ||
1041  | 
    }  | 
    ||
1042  | 
    2599  | 
    ret = fchdir(newfd);  | 
    |
1043  | 
    bail:  | 
    ||
1044  | 
    2599  | 
    oerrno = errno;  | 
    |
1045  | 
    ✓✓ | 2599  | 
    if (fd < 0)  | 
    
1046  | 
    635  | 
    (void)close(newfd);  | 
    |
1047  | 
    2599  | 
    errno = oerrno;  | 
    |
1048  | 
    2599  | 
    return (ret);  | 
    |
1049  | 
    3350  | 
    }  | 
    
| Generated by: GCOVR (Version 3.3) |