1251875Speter/* Licensed to the Apache Software Foundation (ASF) under one or more
2251875Speter * contributor license agreements.  See the NOTICE file distributed with
3251875Speter * this work for additional information regarding copyright ownership.
4251875Speter * The ASF licenses this file to You under the Apache License, Version 2.0
5251875Speter * (the "License"); you may not use this file except in compliance with
6251875Speter * the License.  You may obtain a copy of the License at
7251875Speter *
8251875Speter *     http://www.apache.org/licenses/LICENSE-2.0
9251875Speter *
10251875Speter * Unless required by applicable law or agreed to in writing, software
11251875Speter * distributed under the License is distributed on an "AS IS" BASIS,
12251875Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13251875Speter * See the License for the specific language governing permissions and
14251875Speter * limitations under the License.
15251875Speter */
16251875Speter
17251875Speter#include "apr_arch_file_io.h"
18251875Speter#include "apr_strings.h"
19251875Speter#include "apr_portable.h"
20251875Speter#if APR_HAVE_SYS_SYSLIMITS_H
21251875Speter#include <sys/syslimits.h>
22251875Speter#endif
23251875Speter#if APR_HAVE_LIMITS_H
24251875Speter#include <limits.h>
25251875Speter#endif
26251875Speter
27251875Speterstatic apr_status_t dir_cleanup(void *thedir)
28251875Speter{
29251875Speter    apr_dir_t *dir = thedir;
30251875Speter    if (closedir(dir->dirstruct) == 0) {
31251875Speter        return APR_SUCCESS;
32251875Speter    }
33251875Speter    else {
34251875Speter        return errno;
35251875Speter    }
36251875Speter}
37251875Speter
38251875Speter#define PATH_SEPARATOR '/'
39251875Speter
40251875Speter/* Remove trailing separators that don't affect the meaning of PATH. */
41251875Speterstatic const char *path_canonicalize (const char *path, apr_pool_t *pool)
42251875Speter{
43251875Speter    /* At some point this could eliminate redundant components.  For
44251875Speter     * now, it just makes sure there is no trailing slash. */
45251875Speter    apr_size_t len = strlen (path);
46251875Speter    apr_size_t orig_len = len;
47251875Speter
48251875Speter    while ((len > 0) && (path[len - 1] == PATH_SEPARATOR))
49251875Speter        len--;
50251875Speter
51251875Speter    if (len != orig_len)
52251875Speter        return apr_pstrndup (pool, path, len);
53251875Speter    else
54251875Speter        return path;
55251875Speter}
56251875Speter
57251875Speter/* Remove one component off the end of PATH. */
58251875Speterstatic char *path_remove_last_component (const char *path, apr_pool_t *pool)
59251875Speter{
60251875Speter    const char *newpath = path_canonicalize (path, pool);
61251875Speter    int i;
62251875Speter
63251875Speter    for (i = (strlen(newpath) - 1); i >= 0; i--) {
64251875Speter        if (path[i] == PATH_SEPARATOR)
65251875Speter            break;
66251875Speter    }
67251875Speter
68251875Speter    return apr_pstrndup (pool, path, (i < 0) ? 0 : i);
69251875Speter}
70251875Speter
71251875Speterapr_status_t apr_dir_open(apr_dir_t **new, const char *dirname,
72251875Speter                          apr_pool_t *pool)
73251875Speter{
74251875Speter    /* On some platforms (e.g., Linux+GNU libc), d_name[] in struct
75251875Speter     * dirent is declared with enough storage for the name.  On other
76251875Speter     * platforms (e.g., Solaris 8 for Intel), d_name is declared as a
77251875Speter     * one-byte array.  Note: gcc evaluates this at compile time.
78251875Speter     */
79251875Speter    apr_size_t dirent_size =
80251875Speter        sizeof(*(*new)->entry) +
81251875Speter        (sizeof((*new)->entry->d_name) > 1 ? 0 : 255);
82251875Speter    DIR *dir = opendir(dirname);
83251875Speter
84251875Speter    if (!dir) {
85251875Speter        return errno;
86251875Speter    }
87251875Speter
88251875Speter    (*new) = (apr_dir_t *)apr_palloc(pool, sizeof(apr_dir_t));
89251875Speter
90251875Speter    (*new)->pool = pool;
91251875Speter    (*new)->dirname = apr_pstrdup(pool, dirname);
92251875Speter    (*new)->dirstruct = dir;
93251875Speter    (*new)->entry = apr_pcalloc(pool, dirent_size);
94251875Speter
95251875Speter    apr_pool_cleanup_register((*new)->pool, *new, dir_cleanup,
96251875Speter                              apr_pool_cleanup_null);
97251875Speter    return APR_SUCCESS;
98251875Speter}
99251875Speter
100251875Speterapr_status_t apr_dir_close(apr_dir_t *thedir)
101251875Speter{
102251875Speter    return apr_pool_cleanup_run(thedir->pool, thedir, dir_cleanup);
103251875Speter}
104251875Speter
105251875Speter#ifdef DIRENT_TYPE
106251875Speterstatic apr_filetype_e filetype_from_dirent_type(int type)
107251875Speter{
108251875Speter    switch (type) {
109251875Speter    case DT_REG:
110251875Speter        return APR_REG;
111251875Speter    case DT_DIR:
112251875Speter        return APR_DIR;
113251875Speter    case DT_LNK:
114251875Speter        return APR_LNK;
115251875Speter    case DT_CHR:
116251875Speter        return APR_CHR;
117251875Speter    case DT_BLK:
118251875Speter        return APR_BLK;
119251875Speter#if defined(DT_FIFO)
120251875Speter    case DT_FIFO:
121251875Speter        return APR_PIPE;
122251875Speter#endif
123251875Speter#if !defined(BEOS) && defined(DT_SOCK)
124251875Speter    case DT_SOCK:
125251875Speter        return APR_SOCK;
126251875Speter#endif
127251875Speter    default:
128251875Speter        return APR_UNKFILE;
129251875Speter    }
130251875Speter}
131251875Speter#endif
132251875Speter
133251875Speterapr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted,
134251875Speter                          apr_dir_t *thedir)
135251875Speter{
136251875Speter    apr_status_t ret = 0;
137251875Speter#ifdef DIRENT_TYPE
138251875Speter    apr_filetype_e type;
139251875Speter#endif
140251875Speter#if APR_HAS_THREADS && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \
141251875Speter                    && !defined(READDIR_IS_THREAD_SAFE)
142251875Speter#ifdef APR_USE_READDIR64_R
143251875Speter    struct dirent64 *retent;
144251875Speter
145251875Speter    /* If LFS is enabled and readdir64_r is available, readdir64_r is
146251875Speter     * used in preference to readdir_r.  This allows directories to be
147251875Speter     * read which contain a (64-bit) inode number which doesn't fit
148251875Speter     * into the 32-bit apr_ino_t, iff the caller doesn't actually care
149251875Speter     * about the inode number (i.e. wanted & APR_FINFO_INODE == 0).
150251875Speter     * (such inodes may be seen in some wonky NFS environments)
151251875Speter     *
152251875Speter     * Similarly, if the d_off field cannot be reprented in a 32-bit
153251875Speter     * offset, the libc readdir_r() would barf; using readdir64_r
154251875Speter     * bypasses that case entirely since APR does not care about
155251875Speter     * d_off. */
156251875Speter
157251875Speter    ret = readdir64_r(thedir->dirstruct, thedir->entry, &retent);
158251875Speter#else
159251875Speter
160251875Speter    struct dirent *retent;
161251875Speter
162251875Speter    ret = readdir_r(thedir->dirstruct, thedir->entry, &retent);
163251875Speter#endif
164251875Speter
165251875Speter    /* POSIX treats "end of directory" as a non-error case, so ret
166251875Speter     * will be zero and retent will be set to NULL in that case. */
167251875Speter    if (!ret && retent == NULL) {
168251875Speter        ret = APR_ENOENT;
169251875Speter    }
170251875Speter
171251875Speter    /* Solaris is a bit strange, if there are no more entries in the
172251875Speter     * directory, it returns EINVAL.  Since this is against POSIX, we
173251875Speter     * hack around the problem here.  EINVAL is possible from other
174251875Speter     * readdir implementations, but only if the result buffer is too small.
175251875Speter     * since we control the size of that buffer, we should never have
176251875Speter     * that problem.
177251875Speter     */
178251875Speter    if (ret == EINVAL) {
179251875Speter        ret = APR_ENOENT;
180251875Speter    }
181251875Speter#else
182251875Speter    /* We're about to call a non-thread-safe readdir() that may
183251875Speter       possibly set `errno', and the logic below actually cares about
184251875Speter       errno after the call.  Therefore we need to clear errno first. */
185251875Speter    errno = 0;
186251875Speter    thedir->entry = readdir(thedir->dirstruct);
187251875Speter    if (thedir->entry == NULL) {
188251875Speter        /* If NULL was returned, this can NEVER be a success. Can it?! */
189251875Speter        if (errno == APR_SUCCESS) {
190251875Speter            ret = APR_ENOENT;
191251875Speter        }
192251875Speter        else
193251875Speter            ret = errno;
194251875Speter    }
195251875Speter#endif
196251875Speter
197251875Speter    /* No valid bit flag to test here - do we want one? */
198251875Speter    finfo->fname = NULL;
199251875Speter
200251875Speter    if (ret) {
201251875Speter        finfo->valid = 0;
202251875Speter        return ret;
203251875Speter    }
204251875Speter
205251875Speter#ifdef DIRENT_TYPE
206251875Speter    type = filetype_from_dirent_type(thedir->entry->DIRENT_TYPE);
207251875Speter    if (type != APR_UNKFILE) {
208251875Speter        wanted &= ~APR_FINFO_TYPE;
209251875Speter    }
210251875Speter#endif
211251875Speter#ifdef DIRENT_INODE
212251875Speter    if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
213251875Speter#ifdef APR_USE_READDIR64_R
214251875Speter        /* If readdir64_r is used, check for the overflow case of trying
215251875Speter         * to fit a 64-bit integer into a 32-bit integer. */
216251875Speter        if (sizeof(apr_ino_t) >= sizeof(retent->DIRENT_INODE)
217251875Speter            || (apr_ino_t)retent->DIRENT_INODE == retent->DIRENT_INODE) {
218251875Speter            wanted &= ~APR_FINFO_INODE;
219251875Speter        } else {
220251875Speter            /* Prevent the fallback code below from filling in the
221251875Speter             * inode if the stat call fails. */
222251875Speter            retent->DIRENT_INODE = 0;
223251875Speter        }
224251875Speter#else
225251875Speter        wanted &= ~APR_FINFO_INODE;
226251875Speter#endif /* APR_USE_READDIR64_R */
227251875Speter    }
228251875Speter#endif /* DIRENT_INODE */
229251875Speter
230251875Speter    wanted &= ~APR_FINFO_NAME;
231251875Speter
232251875Speter    if (wanted)
233251875Speter    {
234251875Speter        char fspec[APR_PATH_MAX];
235251875Speter        char *end;
236251875Speter
237251875Speter        end = apr_cpystrn(fspec, thedir->dirname, sizeof fspec);
238251875Speter
239251875Speter        if (end > fspec && end[-1] != '/' && (end < fspec + APR_PATH_MAX))
240251875Speter            *end++ = '/';
241251875Speter
242251875Speter        apr_cpystrn(end, thedir->entry->d_name,
243251875Speter                    sizeof fspec - (end - fspec));
244251875Speter
245251875Speter        ret = apr_stat(finfo, fspec, APR_FINFO_LINK | wanted, thedir->pool);
246251875Speter        /* We passed a stack name that will disappear */
247251875Speter        finfo->fname = NULL;
248251875Speter    }
249251875Speter
250251875Speter    if (wanted && (ret == APR_SUCCESS || ret == APR_INCOMPLETE)) {
251251875Speter        wanted &= ~finfo->valid;
252251875Speter    }
253251875Speter    else {
254251875Speter        /* We don't bail because we fail to stat, when we are only -required-
255251875Speter         * to readdir... but the result will be APR_INCOMPLETE
256251875Speter         */
257251875Speter        finfo->pool = thedir->pool;
258251875Speter        finfo->valid = 0;
259251875Speter#ifdef DIRENT_TYPE
260251875Speter        if (type != APR_UNKFILE) {
261251875Speter            finfo->filetype = type;
262251875Speter            finfo->valid |= APR_FINFO_TYPE;
263251875Speter        }
264251875Speter#endif
265251875Speter#ifdef DIRENT_INODE
266251875Speter        if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
267251875Speter            finfo->inode = thedir->entry->DIRENT_INODE;
268251875Speter            finfo->valid |= APR_FINFO_INODE;
269251875Speter        }
270251875Speter#endif
271251875Speter    }
272251875Speter
273251875Speter    finfo->name = apr_pstrdup(thedir->pool, thedir->entry->d_name);
274251875Speter    finfo->valid |= APR_FINFO_NAME;
275251875Speter
276251875Speter    if (wanted)
277251875Speter        return APR_INCOMPLETE;
278251875Speter
279251875Speter    return APR_SUCCESS;
280251875Speter}
281251875Speter
282251875Speterapr_status_t apr_dir_rewind(apr_dir_t *thedir)
283251875Speter{
284251875Speter    rewinddir(thedir->dirstruct);
285251875Speter    return APR_SUCCESS;
286251875Speter}
287251875Speter
288251875Speterapr_status_t apr_dir_make(const char *path, apr_fileperms_t perm,
289251875Speter                          apr_pool_t *pool)
290251875Speter{
291251875Speter    mode_t mode = apr_unix_perms2mode(perm);
292251875Speter
293251875Speter    if (mkdir(path, mode) == 0) {
294251875Speter        return APR_SUCCESS;
295251875Speter    }
296251875Speter    else {
297251875Speter        return errno;
298251875Speter    }
299251875Speter}
300251875Speter
301251875Speterapr_status_t apr_dir_make_recursive(const char *path, apr_fileperms_t perm,
302251875Speter                                           apr_pool_t *pool)
303251875Speter{
304251875Speter    apr_status_t apr_err = 0;
305251875Speter
306251875Speter    apr_err = apr_dir_make (path, perm, pool); /* Try to make PATH right out */
307251875Speter
308251875Speter    if (apr_err == ENOENT) { /* Missing an intermediate dir */
309251875Speter        char *dir;
310251875Speter
311251875Speter        dir = path_remove_last_component(path, pool);
312251875Speter        /* If there is no path left, give up. */
313251875Speter        if (dir[0] == '\0') {
314251875Speter            return apr_err;
315251875Speter        }
316251875Speter
317251875Speter        apr_err = apr_dir_make_recursive(dir, perm, pool);
318251875Speter
319251875Speter        if (!apr_err)
320251875Speter            apr_err = apr_dir_make (path, perm, pool);
321251875Speter    }
322251875Speter
323251875Speter    /*
324251875Speter     * It's OK if PATH exists. Timing issues can lead to the second
325251875Speter     * apr_dir_make being called on existing dir, therefore this check
326251875Speter     * has to come last.
327251875Speter     */
328251875Speter    if (APR_STATUS_IS_EEXIST(apr_err))
329251875Speter        return APR_SUCCESS;
330251875Speter
331251875Speter    return apr_err;
332251875Speter}
333251875Speter
334251875Speterapr_status_t apr_dir_remove(const char *path, apr_pool_t *pool)
335251875Speter{
336251875Speter    if (rmdir(path) == 0) {
337251875Speter        return APR_SUCCESS;
338251875Speter    }
339251875Speter    else {
340251875Speter        return errno;
341251875Speter    }
342251875Speter}
343251875Speter
344251875Speterapr_status_t apr_os_dir_get(apr_os_dir_t **thedir, apr_dir_t *dir)
345251875Speter{
346251875Speter    if (dir == NULL) {
347251875Speter        return APR_ENODIR;
348251875Speter    }
349251875Speter    *thedir = dir->dirstruct;
350251875Speter    return APR_SUCCESS;
351251875Speter}
352251875Speter
353251875Speterapr_status_t apr_os_dir_put(apr_dir_t **dir, apr_os_dir_t *thedir,
354251875Speter                          apr_pool_t *pool)
355251875Speter{
356251875Speter    if ((*dir) == NULL) {
357251875Speter        (*dir) = (apr_dir_t *)apr_pcalloc(pool, sizeof(apr_dir_t));
358251875Speter        (*dir)->pool = pool;
359251875Speter    }
360251875Speter    (*dir)->dirstruct = thedir;
361251875Speter    return APR_SUCCESS;
362251875Speter}
363251875Speter
364251875Speter
365