View | Details | Raw Unified | Return to bug 127872
Collapse All | Expand All

(-)rewinddir.c 2008-10-05 02:24:15.000000000 +0200 (-1 / +161 lines)
Lines 33-40 Link Here
33
#include <sys/cdefs.h>
33
#include <sys/cdefs.h>
34
__FBSDID("$FreeBSD: src/lib/libc/gen/rewinddir.c,v 1.6 2007/01/09 00:27:55 imp Exp $");
34
__FBSDID("$FreeBSD: src/lib/libc/gen/rewinddir.c,v 1.6 2007/01/09 00:27:55 imp Exp $");
35
#include "namespace.h"
35
#include <sys/types.h>
36
#include <sys/types.h>
37
#include <sys/param.h>
38
#include <sys/mount.h>
39
#include <errno.h>
40
#include <fcntl.h>
41
#include <unistd.h>
36
#include <dirent.h>
42
#include <dirent.h>
43
#include <stdlib.h>
44
#include <string.h>
45
#include "un-namespace.h"
37
#include "telldir.h"
46
#include "telldir.h"
Lines 42-48 Link Here
42
rewinddir(dirp)
51
rewinddir(dirp)
43
       DIR *dirp;
52
       DIR *dirp;
44
{
53
{
45
46
       _seekdir(dirp, dirp->dd_rewind);
54
       _seekdir(dirp, dirp->dd_rewind);
55
56
       int incr;
57
       int unionstack;
58
59
    /*
60
     * Use the system page size if that is a multiple of DIRBLKSIZ.
61
     * Hopefully this can be a big win someday by allowing page
62
     * trades to user space to be done by _getdirentries().
63
     */
64
    incr = getpagesize();
65
    if ((incr % DIRBLKSIZ) != 0)
66
        incr = DIRBLKSIZ;
67
68
    /*
69
     * Determine whether this directory is the top of a union stack.
70
     */
71
    if (dirp->dd_flags & DTF_NODUP) {
72
        struct statfs sfb;
73
74
        if (_fstatfs(dirp->dd_fd, &sfb) < 0)
75
            goto fail;
76
        unionstack = !strcmp(sfb.f_fstypename, "unionfs")
77
            || (sfb.f_flags & MNT_UNION);
78
    } else {
79
        unionstack = 0;
80
    }
81
82
    if (unionstack) {
83
        int len = dirp->dd_len;
84
        int space = dirp->dd_len;
85
        char *buf = dirp->dd_buf;
86
        char *ddptr = dirp->dd_buf;
87
        char *ddeptr;
88
        int n;
89
        struct dirent **dpv;
90
91
               if (lseek(dirp->dd_fd, 0, SEEK_SET) == -1)
92
                       goto fail;
93
               dirp->dd_seek = 0;
94
95
        /*
96
         * The strategy here is to read all the directory
97
         * entries into a buffer, sort the buffer, and
98
         * remove duplicate entries by setting the inode
99
         * number to zero.
100
         */
101
102
        do {
103
            /*
104
             * Always make at least DIRBLKSIZ bytes
105
             * available to _getdirentries
106
             */
107
            if (space < DIRBLKSIZ) {
108
                space += incr;
109
                len += incr;
110
                buf = reallocf(buf, len);
111
                if (buf == NULL)
112
                    goto fail;
113
                ddptr = buf + (len - space);
114
            }
115
116
            n = _getdirentries(dirp->dd_fd, ddptr, space, &dirp->dd_seek);
117
            if (n > 0) {
118
                ddptr += n;
119
                space -= n;
120
            }
121
        } while (n > 0);
122
123
        ddeptr = ddptr;
124
        dirp->dd_flags |= __DTF_READALL;
125
126
        /*
127
         * There is now a buffer full of (possibly) duplicate
128
         * names.
129
         */
130
        dirp->dd_buf = buf;
131
132
        /*
133
         * Go round this loop twice...
134
         *
135
         * Scan through the buffer, counting entries.
136
         * On the second pass, save pointers to each one.
137
         * Then sort the pointers and remove duplicate names.
138
         */
139
        for (dpv = 0;;) {
140
            n = 0;
141
            ddptr = buf;
142
            while (ddptr < ddeptr) {
143
                struct dirent *dp;
144
145
                dp = (struct dirent *) ddptr;
146
                if ((long)dp & 03L)
147
                    break;
148
                if ((dp->d_reclen <= 0) ||
149
                    (dp->d_reclen > (ddeptr + 1 - ddptr)))
150
                    break;
151
                ddptr += dp->d_reclen;
152
                if (dp->d_fileno) {
153
                    if (dpv)
154
                        dpv[n] = dp;
155
                    n++;
156
                }
157
            }
158
159
            if (dpv) {
160
                struct dirent *xp;
161
162
                /*
163
                 * This sort must be stable.
164
                 */
165
                mergesort(dpv, n, sizeof(*dpv), alphasort);
166
167
                dpv[n] = NULL;
168
                xp = NULL;
169
170
                /*
171
                 * Scan through the buffer in sort order,
172
                 * zapping the inode number of any
173
                 * duplicate names.
174
                 */
175
                for (n = 0; dpv[n]; n++) {
176
                    struct dirent *dp = dpv[n];
177
178
                    if ((xp == NULL) ||
179
                        strcmp(dp->d_name, xp->d_name)) {
180
                        xp = dp;
181
                    } else {
182
                        dp->d_fileno = 0;
183
                    }
184
                    if (dp->d_type == DT_WHT &&
185
                        (dirp->dd_flags & DTF_HIDEW))
186
                        dp->d_fileno = 0;
187
                }
188
189
                free(dpv);
190
                break;
191
            } else {
192
                dpv = malloc((n+1) * sizeof(struct dirent *));
193
                if (dpv == NULL)
194
                    break;
195
            }
196
        }
197
198
        dirp->dd_len = len;
199
               dirp->dd_loc = 0;
200
        dirp->dd_size = ddptr - dirp->dd_buf;
201
    }
202
203
/*
204
 * Silently ignore any errors
205
 */
206
fail:
47
       dirp->dd_rewind = telldir(dirp);
207
       dirp->dd_rewind = telldir(dirp);
48
}
208
}

Return to bug 127872