Coverage Report

Created: 2023-03-26 06:28

/src/httpd/srclib/apr/file_io/unix/dir.c
Line
Count
Source (jump to first uncovered line)
1
/* Licensed to the Apache Software Foundation (ASF) under one or more
2
 * contributor license agreements.  See the NOTICE file distributed with
3
 * this work for additional information regarding copyright ownership.
4
 * The ASF licenses this file to You under the Apache License, Version 2.0
5
 * (the "License"); you may not use this file except in compliance with
6
 * the License.  You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include "apr_arch_file_io.h"
18
#include "apr_strings.h"
19
#include "apr_portable.h"
20
#if APR_HAVE_SYS_SYSLIMITS_H
21
#include <sys/syslimits.h>
22
#endif
23
#if APR_HAVE_LIMITS_H
24
#include <limits.h>
25
#endif
26
27
#ifndef NAME_MAX
28
#define NAME_MAX 255
29
#endif
30
31
static apr_status_t dir_cleanup(void *thedir)
32
0
{
33
0
    apr_dir_t *dir = thedir;
34
0
    if (closedir(dir->dirstruct) == 0) {
35
0
        return APR_SUCCESS;
36
0
    }
37
0
    else {
38
0
        return errno;
39
0
    }
40
0
}
41
42
0
#define PATH_SEPARATOR '/'
43
44
/* Remove trailing separators that don't affect the meaning of PATH. */
45
static const char *path_canonicalize (const char *path, apr_pool_t *pool)
46
0
{
47
    /* At some point this could eliminate redundant components.  For
48
     * now, it just makes sure there is no trailing slash. */
49
0
    apr_size_t len = strlen (path);
50
0
    apr_size_t orig_len = len;
51
52
0
    while ((len > 0) && (path[len - 1] == PATH_SEPARATOR))
53
0
        len--;
54
55
0
    if (len != orig_len)
56
0
        return apr_pstrndup (pool, path, len);
57
0
    else
58
0
        return path;
59
0
}
60
61
/* Remove one component off the end of PATH. */
62
static char *path_remove_last_component (const char *path, apr_pool_t *pool)
63
0
{
64
0
    const char *newpath = path_canonicalize (path, pool);
65
0
    int i;
66
67
0
    for (i = (strlen(newpath) - 1); i >= 0; i--) {
68
0
        if (path[i] == PATH_SEPARATOR)
69
0
            break;
70
0
    }
71
72
0
    return apr_pstrndup (pool, path, (i < 0) ? 0 : i);
73
0
}
74
75
apr_status_t apr_dir_open(apr_dir_t **new, const char *dirname,
76
                          apr_pool_t *pool)
77
0
{
78
0
    DIR *dir = opendir(dirname);
79
80
0
    if (!dir) {
81
0
        return errno;
82
0
    }
83
84
0
    (*new) = (apr_dir_t *)apr_palloc(pool, sizeof(apr_dir_t));
85
86
0
    (*new)->pool = pool;
87
0
    (*new)->dirname = apr_pstrdup(pool, dirname);
88
0
    (*new)->dirstruct = dir;
89
90
#if APR_HAS_THREADS && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \
91
                    && !defined(READDIR_IS_THREAD_SAFE)
92
    /* On some platforms (e.g., Linux+GNU libc), d_name[] in struct
93
     * dirent is declared with enough storage for the name.  On other
94
     * platforms (e.g., Solaris 8 for Intel), d_name is declared as a
95
     * one-byte array.  Note: gcc evaluates this at compile time.
96
     */
97
    (*new)->entry = apr_pcalloc(pool, sizeof(*(*new)->entry) +
98
                                      (sizeof((*new)->entry->d_name) > 1
99
                                       ? 0 : NAME_MAX));
100
#else
101
0
    (*new)->entry = NULL;
102
0
#endif
103
104
0
    apr_pool_cleanup_register((*new)->pool, *new, dir_cleanup,
105
0
                              apr_pool_cleanup_null);
106
0
    return APR_SUCCESS;
107
0
}
108
109
apr_status_t apr_dir_close(apr_dir_t *thedir)
110
0
{
111
0
    return apr_pool_cleanup_run(thedir->pool, thedir, dir_cleanup);
112
0
}
113
114
#ifdef DIRENT_TYPE
115
static apr_filetype_e filetype_from_dirent_type(int type)
116
0
{
117
0
    switch (type) {
118
0
    case DT_REG:
119
0
        return APR_REG;
120
0
    case DT_DIR:
121
0
        return APR_DIR;
122
0
    case DT_LNK:
123
0
        return APR_LNK;
124
0
    case DT_CHR:
125
0
        return APR_CHR;
126
0
    case DT_BLK:
127
0
        return APR_BLK;
128
0
#if defined(DT_FIFO)
129
0
    case DT_FIFO:
130
0
        return APR_PIPE;
131
0
#endif
132
0
#if !defined(BEOS) && defined(DT_SOCK)
133
0
    case DT_SOCK:
134
0
        return APR_SOCK;
135
0
#endif
136
0
    default:
137
0
        return APR_UNKFILE;
138
0
    }
139
0
}
140
#endif
141
142
apr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted,
143
                          apr_dir_t *thedir)
144
0
{
145
0
    apr_status_t ret = 0;
146
0
#ifdef DIRENT_TYPE
147
0
    apr_filetype_e type;
148
0
#endif
149
#if APR_HAS_THREADS && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \
150
                    && !defined(READDIR_IS_THREAD_SAFE)
151
#ifdef APR_USE_READDIR64_R
152
    struct dirent64 *retent;
153
154
    /* If LFS is enabled and readdir64_r is available, readdir64_r is
155
     * used in preference to readdir_r.  This allows directories to be
156
     * read which contain a (64-bit) inode number which doesn't fit
157
     * into the 32-bit apr_ino_t, iff the caller doesn't actually care
158
     * about the inode number (i.e. wanted & APR_FINFO_INODE == 0).
159
     * (such inodes may be seen in some wonky NFS environments)
160
     *
161
     * Similarly, if the d_off field cannot be reprented in a 32-bit
162
     * offset, the libc readdir_r() would barf; using readdir64_r
163
     * bypasses that case entirely since APR does not care about
164
     * d_off. */
165
166
    ret = readdir64_r(thedir->dirstruct, thedir->entry, &retent);
167
#else
168
169
    struct dirent *retent;
170
171
    ret = readdir_r(thedir->dirstruct, thedir->entry, &retent);
172
#endif
173
174
    /* POSIX treats "end of directory" as a non-error case, so ret
175
     * will be zero and retent will be set to NULL in that case. */
176
    if (!ret && retent == NULL) {
177
        ret = APR_ENOENT;
178
    }
179
180
    /* Solaris is a bit strange, if there are no more entries in the
181
     * directory, it returns EINVAL.  Since this is against POSIX, we
182
     * hack around the problem here.  EINVAL is possible from other
183
     * readdir implementations, but only if the result buffer is too small.
184
     * since we control the size of that buffer, we should never have
185
     * that problem.
186
     */
187
    if (ret == EINVAL) {
188
        ret = APR_ENOENT;
189
    }
190
#else
191
    /* We're about to call a non-thread-safe readdir() that may
192
       possibly set `errno', and the logic below actually cares about
193
       errno after the call.  Therefore we need to clear errno first. */
194
0
    errno = 0;
195
0
    thedir->entry = readdir(thedir->dirstruct);
196
0
    if (thedir->entry == NULL) {
197
        /* If NULL was returned, this can NEVER be a success. Can it?! */
198
0
        if (errno == APR_SUCCESS) {
199
0
            ret = APR_ENOENT;
200
0
        }
201
0
        else
202
0
            ret = errno;
203
0
    }
204
0
#endif
205
206
    /* No valid bit flag to test here - do we want one? */
207
0
    finfo->fname = NULL;
208
209
0
    if (ret) {
210
0
        finfo->valid = 0;
211
0
        return ret;
212
0
    }
213
214
0
#ifdef DIRENT_TYPE
215
0
    type = filetype_from_dirent_type(thedir->entry->DIRENT_TYPE);
216
0
    if (type != APR_UNKFILE) {
217
0
        wanted &= ~APR_FINFO_TYPE;
218
0
    }
219
0
#endif
220
0
#ifdef DIRENT_INODE
221
0
    if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
222
#ifdef APR_USE_READDIR64_R
223
        /* If readdir64_r is used, check for the overflow case of trying
224
         * to fit a 64-bit integer into a 32-bit integer. */
225
        if (sizeof(apr_ino_t) >= sizeof(retent->DIRENT_INODE)
226
            || (apr_ino_t)retent->DIRENT_INODE == retent->DIRENT_INODE) {
227
            wanted &= ~APR_FINFO_INODE;
228
        } else {
229
            /* Prevent the fallback code below from filling in the
230
             * inode if the stat call fails. */
231
            retent->DIRENT_INODE = 0;
232
        }
233
#else
234
0
        wanted &= ~APR_FINFO_INODE;
235
0
#endif /* APR_USE_READDIR64_R */
236
0
    }
237
0
#endif /* DIRENT_INODE */
238
239
0
    wanted &= ~APR_FINFO_NAME;
240
241
0
    if (wanted)
242
0
    {
243
0
        char fspec[APR_PATH_MAX];
244
0
        char *end;
245
246
0
        end = apr_cpystrn(fspec, thedir->dirname, sizeof fspec);
247
248
0
        if (end > fspec && end[-1] != '/' && (end < fspec + APR_PATH_MAX))
249
0
            *end++ = '/';
250
251
0
        apr_cpystrn(end, thedir->entry->d_name,
252
0
                    sizeof fspec - (end - fspec));
253
254
0
        ret = apr_stat(finfo, fspec, APR_FINFO_LINK | wanted, thedir->pool);
255
        /* We passed a stack name that will disappear */
256
0
        finfo->fname = NULL;
257
0
    }
258
259
0
    if (wanted && (ret == APR_SUCCESS || ret == APR_INCOMPLETE)) {
260
0
        wanted &= ~finfo->valid;
261
0
    }
262
0
    else {
263
        /* We don't bail because we fail to stat, when we are only -required-
264
         * to readdir... but the result will be APR_INCOMPLETE
265
         */
266
0
        finfo->pool = thedir->pool;
267
0
        finfo->valid = 0;
268
0
#ifdef DIRENT_TYPE
269
0
        if (type != APR_UNKFILE) {
270
0
            finfo->filetype = type;
271
0
            finfo->valid |= APR_FINFO_TYPE;
272
0
        }
273
0
#endif
274
0
#ifdef DIRENT_INODE
275
0
        if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
276
0
            finfo->inode = thedir->entry->DIRENT_INODE;
277
0
            finfo->valid |= APR_FINFO_INODE;
278
0
        }
279
0
#endif
280
0
    }
281
282
0
    finfo->name = apr_pstrdup(thedir->pool, thedir->entry->d_name);
283
0
    finfo->valid |= APR_FINFO_NAME;
284
285
0
    if (wanted)
286
0
        return APR_INCOMPLETE;
287
288
0
    return APR_SUCCESS;
289
0
}
290
291
apr_status_t apr_dir_rewind(apr_dir_t *thedir)
292
0
{
293
0
    rewinddir(thedir->dirstruct);
294
0
    return APR_SUCCESS;
295
0
}
296
297
apr_status_t apr_dir_make(const char *path, apr_fileperms_t perm,
298
                          apr_pool_t *pool)
299
0
{
300
0
    mode_t mode = apr_unix_perms2mode(perm);
301
302
0
    if (mkdir(path, mode) == 0) {
303
0
        return APR_SUCCESS;
304
0
    }
305
0
    else {
306
0
        return errno;
307
0
    }
308
0
}
309
310
apr_status_t apr_dir_make_recursive(const char *path, apr_fileperms_t perm,
311
                                           apr_pool_t *pool)
312
0
{
313
0
    apr_status_t apr_err = 0;
314
315
0
    apr_err = apr_dir_make (path, perm, pool); /* Try to make PATH right out */
316
317
0
    if (apr_err == ENOENT) { /* Missing an intermediate dir */
318
0
        char *dir;
319
320
0
        dir = path_remove_last_component(path, pool);
321
        /* If there is no path left, give up. */
322
0
        if (dir[0] == '\0') {
323
0
            return apr_err;
324
0
        }
325
326
0
        apr_err = apr_dir_make_recursive(dir, perm, pool);
327
328
0
        if (!apr_err)
329
0
            apr_err = apr_dir_make (path, perm, pool);
330
0
    }
331
332
    /*
333
     * It's OK if PATH exists. Timing issues can lead to the second
334
     * apr_dir_make being called on existing dir, therefore this check
335
     * has to come last.
336
     */
337
0
    if (APR_STATUS_IS_EEXIST(apr_err))
338
0
        return APR_SUCCESS;
339
340
0
    return apr_err;
341
0
}
342
343
apr_status_t apr_dir_remove(const char *path, apr_pool_t *pool)
344
0
{
345
0
    if (rmdir(path) == 0) {
346
0
        return APR_SUCCESS;
347
0
    }
348
0
    else {
349
0
        return errno;
350
0
    }
351
0
}
352
353
apr_status_t apr_os_dir_get(apr_os_dir_t **thedir, apr_dir_t *dir)
354
0
{
355
0
    if (dir == NULL) {
356
0
        return APR_ENODIR;
357
0
    }
358
0
    *thedir = dir->dirstruct;
359
0
    return APR_SUCCESS;
360
0
}
361
362
apr_status_t apr_os_dir_put(apr_dir_t **dir, apr_os_dir_t *thedir,
363
                          apr_pool_t *pool)
364
0
{
365
0
    if ((*dir) == NULL) {
366
0
        (*dir) = (apr_dir_t *)apr_pcalloc(pool, sizeof(apr_dir_t));
367
0
        (*dir)->pool = pool;
368
0
    }
369
0
    (*dir)->dirstruct = thedir;
370
0
    return APR_SUCCESS;
371
0
}
372
373