1160814Ssimon/* Licensed to the Apache Software Foundation (ASF) under one or more
2160814Ssimon * contributor license agreements.  See the NOTICE file distributed with
3160814Ssimon * this work for additional information regarding copyright ownership.
4160814Ssimon * The ASF licenses this file to You under the Apache License, Version 2.0
5160814Ssimon * (the "License"); you may not use this file except in compliance with
6160814Ssimon * the License.  You may obtain a copy of the License at
7160814Ssimon *
8160814Ssimon *     http://www.apache.org/licenses/LICENSE-2.0
9160814Ssimon *
10160814Ssimon * Unless required by applicable law or agreed to in writing, software
11160814Ssimon * distributed under the License is distributed on an "AS IS" BASIS,
12160814Ssimon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13160814Ssimon * See the License for the specific language governing permissions and
14160814Ssimon * limitations under the License.
15160814Ssimon */
16160814Ssimon
17160814Ssimon#include "apr_arch_file_io.h"
18160814Ssimon#include "apr_strings.h"
19160814Ssimon#include "apr_portable.h"
20160814Ssimon#if APR_HAVE_SYS_SYSLIMITS_H
21160814Ssimon#include <sys/syslimits.h>
22160814Ssimon#endif
23160814Ssimon#if APR_HAVE_LIMITS_H
24160814Ssimon#include <limits.h>
25238405Sjkim#endif
26160814Ssimon
27160814Ssimonstatic apr_status_t dir_cleanup(void *thedir)
28160814Ssimon{
29160814Ssimon    apr_dir_t *dir = thedir;
30160814Ssimon    if (closedir(dir->dirstruct) == 0) {
31160814Ssimon        return APR_SUCCESS;
32160814Ssimon    }
33160814Ssimon    else {
34160814Ssimon        return errno;
35160814Ssimon    }
36238405Sjkim}
37160814Ssimon
38160814Ssimon#define PATH_SEPARATOR '/'
39160814Ssimon
40160814Ssimon/* Remove trailing separators that don't affect the meaning of PATH. */
41160814Ssimonstatic const char *path_canonicalize (const char *path, apr_pool_t *pool)
42160814Ssimon{
43160814Ssimon    /* At some point this could eliminate redundant components.  For
44160814Ssimon     * now, it just makes sure there is no trailing slash. */
45160814Ssimon    apr_size_t len = strlen (path);
46160814Ssimon    apr_size_t orig_len = len;
47160814Ssimon
48160814Ssimon    while ((len > 0) && (path[len - 1] == PATH_SEPARATOR))
49160814Ssimon        len--;
50160814Ssimon
51160814Ssimon    if (len != orig_len)
52160814Ssimon        return apr_pstrndup (pool, path, len);
53160814Ssimon    else
54160814Ssimon        return path;
55160814Ssimon}
56160814Ssimon
57160814Ssimon/* Remove one component off the end of PATH. */
58160814Ssimonstatic char *path_remove_last_component (const char *path, apr_pool_t *pool)
59160814Ssimon{
60160814Ssimon    const char *newpath = path_canonicalize (path, pool);
61160814Ssimon    int i;
62160814Ssimon
63160814Ssimon    for (i = (strlen(newpath) - 1); i >= 0; i--) {
64296341Sdelphij        if (path[i] == PATH_SEPARATOR)
65296341Sdelphij            break;
66160814Ssimon    }
67160814Ssimon
68160814Ssimon    return apr_pstrndup (pool, path, (i < 0) ? 0 : i);
69160814Ssimon}
70160814Ssimon
71160814Ssimonapr_status_t apr_dir_open(apr_dir_t **new, const char *dirname,
72160814Ssimon                          apr_pool_t *pool)
73160814Ssimon{
74160814Ssimon    /* On some platforms (e.g., Linux+GNU libc), d_name[] in struct
75160814Ssimon     * dirent is declared with enough storage for the name.  On other
76160814Ssimon     * platforms (e.g., Solaris 8 for Intel), d_name is declared as a
77160814Ssimon     * one-byte array.  Note: gcc evaluates this at compile time.
78160814Ssimon     */
79160814Ssimon    apr_size_t dirent_size =
80160814Ssimon        sizeof(*(*new)->entry) +
81160814Ssimon        (sizeof((*new)->entry->d_name) > 1 ? 0 : 255);
82160814Ssimon    DIR *dir = opendir(dirname);
83160814Ssimon
84238405Sjkim    if (!dir) {
85238405Sjkim        return errno;
86    }
87
88    (*new) = (apr_dir_t *)apr_palloc(pool, sizeof(apr_dir_t));
89
90    (*new)->pool = pool;
91    (*new)->dirname = apr_pstrdup(pool, dirname);
92    (*new)->dirstruct = dir;
93    (*new)->entry = apr_pcalloc(pool, dirent_size);
94
95    apr_pool_cleanup_register((*new)->pool, *new, dir_cleanup,
96                              apr_pool_cleanup_null);
97    return APR_SUCCESS;
98}
99
100apr_status_t apr_dir_close(apr_dir_t *thedir)
101{
102    return apr_pool_cleanup_run(thedir->pool, thedir, dir_cleanup);
103}
104
105#ifdef DIRENT_TYPE
106static apr_filetype_e filetype_from_dirent_type(int type)
107{
108    switch (type) {
109    case DT_REG:
110        return APR_REG;
111    case DT_DIR:
112        return APR_DIR;
113    case DT_LNK:
114        return APR_LNK;
115    case DT_CHR:
116        return APR_CHR;
117    case DT_BLK:
118        return APR_BLK;
119#if defined(DT_FIFO)
120    case DT_FIFO:
121        return APR_PIPE;
122#endif
123#if !defined(BEOS) && defined(DT_SOCK)
124    case DT_SOCK:
125        return APR_SOCK;
126#endif
127    default:
128        return APR_UNKFILE;
129    }
130}
131#endif
132
133apr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted,
134                          apr_dir_t *thedir)
135{
136    apr_status_t ret = 0;
137#ifdef DIRENT_TYPE
138    apr_filetype_e type;
139#endif
140#if APR_HAS_THREADS && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \
141                    && !defined(READDIR_IS_THREAD_SAFE)
142#ifdef APR_USE_READDIR64_R
143    struct dirent64 *retent;
144
145    /* If LFS is enabled and readdir64_r is available, readdir64_r is
146     * used in preference to readdir_r.  This allows directories to be
147     * read which contain a (64-bit) inode number which doesn't fit
148     * into the 32-bit apr_ino_t, iff the caller doesn't actually care
149     * about the inode number (i.e. wanted & APR_FINFO_INODE == 0).
150     * (such inodes may be seen in some wonky NFS environments)
151     *
152     * Similarly, if the d_off field cannot be reprented in a 32-bit
153     * offset, the libc readdir_r() would barf; using readdir64_r
154     * bypasses that case entirely since APR does not care about
155     * d_off. */
156
157    ret = readdir64_r(thedir->dirstruct, thedir->entry, &retent);
158#else
159
160    struct dirent *retent;
161
162    ret = readdir_r(thedir->dirstruct, thedir->entry, &retent);
163#endif
164
165    /* POSIX treats "end of directory" as a non-error case, so ret
166     * will be zero and retent will be set to NULL in that case. */
167    if (!ret && retent == NULL) {
168        ret = APR_ENOENT;
169    }
170
171    /* Solaris is a bit strange, if there are no more entries in the
172     * directory, it returns EINVAL.  Since this is against POSIX, we
173     * hack around the problem here.  EINVAL is possible from other
174     * readdir implementations, but only if the result buffer is too small.
175     * since we control the size of that buffer, we should never have
176     * that problem.
177     */
178    if (ret == EINVAL) {
179        ret = APR_ENOENT;
180    }
181#else
182    /* We're about to call a non-thread-safe readdir() that may
183       possibly set `errno', and the logic below actually cares about
184       errno after the call.  Therefore we need to clear errno first. */
185    errno = 0;
186    thedir->entry = readdir(thedir->dirstruct);
187    if (thedir->entry == NULL) {
188        /* If NULL was returned, this can NEVER be a success. Can it?! */
189        if (errno == APR_SUCCESS) {
190            ret = APR_ENOENT;
191        }
192        else
193            ret = errno;
194    }
195#endif
196
197    /* No valid bit flag to test here - do we want one? */
198    finfo->fname = NULL;
199
200    if (ret) {
201        finfo->valid = 0;
202        return ret;
203    }
204
205#ifdef DIRENT_TYPE
206    type = filetype_from_dirent_type(thedir->entry->DIRENT_TYPE);
207    if (type != APR_UNKFILE) {
208        wanted &= ~APR_FINFO_TYPE;
209    }
210#endif
211#ifdef DIRENT_INODE
212    if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
213#ifdef APR_USE_READDIR64_R
214        /* If readdir64_r is used, check for the overflow case of trying
215         * to fit a 64-bit integer into a 32-bit integer. */
216        if (sizeof(apr_ino_t) >= sizeof(retent->DIRENT_INODE)
217            || (apr_ino_t)retent->DIRENT_INODE == retent->DIRENT_INODE) {
218            wanted &= ~APR_FINFO_INODE;
219        } else {
220            /* Prevent the fallback code below from filling in the
221             * inode if the stat call fails. */
222            retent->DIRENT_INODE = 0;
223        }
224#else
225        wanted &= ~APR_FINFO_INODE;
226#endif /* APR_USE_READDIR64_R */
227    }
228#endif /* DIRENT_INODE */
229
230    wanted &= ~APR_FINFO_NAME;
231
232    if (wanted)
233    {
234        char fspec[APR_PATH_MAX];
235        char *end;
236
237        end = apr_cpystrn(fspec, thedir->dirname, sizeof fspec);
238
239        if (end > fspec && end[-1] != '/' && (end < fspec + APR_PATH_MAX))
240            *end++ = '/';
241
242        apr_cpystrn(end, thedir->entry->d_name,
243                    sizeof fspec - (end - fspec));
244
245        ret = apr_stat(finfo, fspec, APR_FINFO_LINK | wanted, thedir->pool);
246        /* We passed a stack name that will disappear */
247        finfo->fname = NULL;
248    }
249
250    if (wanted && (ret == APR_SUCCESS || ret == APR_INCOMPLETE)) {
251        wanted &= ~finfo->valid;
252    }
253    else {
254        /* We don't bail because we fail to stat, when we are only -required-
255         * to readdir... but the result will be APR_INCOMPLETE
256         */
257        finfo->pool = thedir->pool;
258        finfo->valid = 0;
259#ifdef DIRENT_TYPE
260        if (type != APR_UNKFILE) {
261            finfo->filetype = type;
262            finfo->valid |= APR_FINFO_TYPE;
263        }
264#endif
265#ifdef DIRENT_INODE
266        if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
267            finfo->inode = thedir->entry->DIRENT_INODE;
268            finfo->valid |= APR_FINFO_INODE;
269        }
270#endif
271    }
272
273    finfo->name = apr_pstrdup(thedir->pool, thedir->entry->d_name);
274    finfo->valid |= APR_FINFO_NAME;
275
276    if (wanted)
277        return APR_INCOMPLETE;
278
279    return APR_SUCCESS;
280}
281
282apr_status_t apr_dir_rewind(apr_dir_t *thedir)
283{
284    rewinddir(thedir->dirstruct);
285    return APR_SUCCESS;
286}
287
288apr_status_t apr_dir_make(const char *path, apr_fileperms_t perm,
289                          apr_pool_t *pool)
290{
291    mode_t mode = apr_unix_perms2mode(perm);
292
293    if (mkdir(path, mode) == 0) {
294        return APR_SUCCESS;
295    }
296    else {
297        return errno;
298    }
299}
300
301apr_status_t apr_dir_make_recursive(const char *path, apr_fileperms_t perm,
302                                           apr_pool_t *pool)
303{
304    apr_status_t apr_err = 0;
305
306    apr_err = apr_dir_make (path, perm, pool); /* Try to make PATH right out */
307
308    if (apr_err == ENOENT) { /* Missing an intermediate dir */
309        char *dir;
310
311        dir = path_remove_last_component(path, pool);
312        /* If there is no path left, give up. */
313        if (dir[0] == '\0') {
314            return apr_err;
315        }
316
317        apr_err = apr_dir_make_recursive(dir, perm, pool);
318
319        if (!apr_err)
320            apr_err = apr_dir_make (path, perm, pool);
321    }
322
323    /*
324     * It's OK if PATH exists. Timing issues can lead to the second
325     * apr_dir_make being called on existing dir, therefore this check
326     * has to come last.
327     */
328    if (APR_STATUS_IS_EEXIST(apr_err))
329        return APR_SUCCESS;
330
331    return apr_err;
332}
333
334apr_status_t apr_dir_remove(const char *path, apr_pool_t *pool)
335{
336    if (rmdir(path) == 0) {
337        return APR_SUCCESS;
338    }
339    else {
340        return errno;
341    }
342}
343
344apr_status_t apr_os_dir_get(apr_os_dir_t **thedir, apr_dir_t *dir)
345{
346    if (dir == NULL) {
347        return APR_ENODIR;
348    }
349    *thedir = dir->dirstruct;
350    return APR_SUCCESS;
351}
352
353apr_status_t apr_os_dir_put(apr_dir_t **dir, apr_os_dir_t *thedir,
354                          apr_pool_t *pool)
355{
356    if ((*dir) == NULL) {
357        (*dir) = (apr_dir_t *)apr_pcalloc(pool, sizeof(apr_dir_t));
358        (*dir)->pool = pool;
359    }
360    (*dir)->dirstruct = thedir;
361    return APR_SUCCESS;
362}
363
364
365