forked from Mirrors/freeswitch
0488a5109a
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3733 d0543943-73ff-0310-b7d9-9358b9ac24b2
324 lines
8.8 KiB
C
324 lines
8.8 KiB
C
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
* (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "apr_arch_file_io.h"
|
|
#include "apr_strings.h"
|
|
#include "apr_portable.h"
|
|
#if APR_HAVE_SYS_SYSLIMITS_H
|
|
#include <sys/syslimits.h>
|
|
#endif
|
|
#if APR_HAVE_LIMITS_H
|
|
#include <limits.h>
|
|
#endif
|
|
|
|
static apr_status_t dir_cleanup(void *thedir)
|
|
{
|
|
apr_dir_t *dir = thedir;
|
|
if (closedir(dir->dirstruct) == 0) {
|
|
return APR_SUCCESS;
|
|
}
|
|
else {
|
|
return errno;
|
|
}
|
|
}
|
|
|
|
#define PATH_SEPARATOR '/'
|
|
|
|
/* Remove trailing separators that don't affect the meaning of PATH. */
|
|
static const char *path_canonicalize (const char *path, apr_pool_t *pool)
|
|
{
|
|
/* At some point this could eliminate redundant components. For
|
|
* now, it just makes sure there is no trailing slash. */
|
|
apr_size_t len = strlen (path);
|
|
apr_size_t orig_len = len;
|
|
|
|
while ((len > 0) && (path[len - 1] == PATH_SEPARATOR))
|
|
len--;
|
|
|
|
if (len != orig_len)
|
|
return apr_pstrndup (pool, path, len);
|
|
else
|
|
return path;
|
|
}
|
|
|
|
/* Remove one component off the end of PATH. */
|
|
static char *path_remove_last_component (const char *path, apr_pool_t *pool)
|
|
{
|
|
const char *newpath = path_canonicalize (path, pool);
|
|
int i;
|
|
|
|
for (i = (strlen(newpath) - 1); i >= 0; i--) {
|
|
if (path[i] == PATH_SEPARATOR)
|
|
break;
|
|
}
|
|
|
|
return apr_pstrndup (pool, path, (i < 0) ? 0 : i);
|
|
}
|
|
|
|
apr_status_t apr_dir_open(apr_dir_t **new, const char *dirname,
|
|
apr_pool_t *pool)
|
|
{
|
|
/* On some platforms (e.g., Linux+GNU libc), d_name[] in struct
|
|
* dirent is declared with enough storage for the name. On other
|
|
* platforms (e.g., Solaris 8 for Intel), d_name is declared as a
|
|
* one-byte array. Note: gcc evaluates this at compile time.
|
|
*/
|
|
apr_size_t dirent_size =
|
|
(sizeof((*new)->entry->d_name) > 1 ?
|
|
sizeof(struct dirent) : sizeof (struct dirent) + 255);
|
|
DIR *dir = opendir(dirname);
|
|
|
|
if (!dir) {
|
|
return errno;
|
|
}
|
|
|
|
(*new) = (apr_dir_t *)apr_palloc(pool, sizeof(apr_dir_t));
|
|
|
|
(*new)->pool = pool;
|
|
(*new)->dirname = apr_pstrdup(pool, dirname);
|
|
(*new)->dirstruct = dir;
|
|
(*new)->entry = apr_pcalloc(pool, dirent_size);
|
|
|
|
apr_pool_cleanup_register((*new)->pool, *new, dir_cleanup,
|
|
apr_pool_cleanup_null);
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
apr_status_t apr_dir_close(apr_dir_t *thedir)
|
|
{
|
|
return apr_pool_cleanup_run(thedir->pool, thedir, dir_cleanup);
|
|
}
|
|
|
|
#ifdef DIRENT_TYPE
|
|
static apr_filetype_e filetype_from_dirent_type(int type)
|
|
{
|
|
switch (type) {
|
|
case DT_REG:
|
|
return APR_REG;
|
|
case DT_DIR:
|
|
return APR_DIR;
|
|
case DT_LNK:
|
|
return APR_LNK;
|
|
case DT_CHR:
|
|
return APR_CHR;
|
|
case DT_BLK:
|
|
return APR_BLK;
|
|
#if defined(DT_FIFO)
|
|
case DT_FIFO:
|
|
return APR_PIPE;
|
|
#endif
|
|
#if !defined(BEOS) && defined(DT_SOCK)
|
|
case DT_SOCK:
|
|
return APR_SOCK;
|
|
#endif
|
|
default:
|
|
return APR_UNKFILE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
apr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted,
|
|
apr_dir_t *thedir)
|
|
{
|
|
apr_status_t ret = 0;
|
|
#ifdef DIRENT_TYPE
|
|
apr_filetype_e type;
|
|
#endif
|
|
#if APR_HAS_THREADS && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \
|
|
&& !defined(READDIR_IS_THREAD_SAFE)
|
|
struct dirent *retent;
|
|
|
|
ret = readdir_r(thedir->dirstruct, thedir->entry, &retent);
|
|
|
|
/* Avoid the Linux problem where at end-of-directory thedir->entry
|
|
* is set to NULL, but ret = APR_SUCCESS.
|
|
*/
|
|
if(!ret && thedir->entry != retent)
|
|
ret = APR_ENOENT;
|
|
|
|
/* Solaris is a bit strange, if there are no more entries in the
|
|
* directory, it returns EINVAL. Since this is against POSIX, we
|
|
* hack around the problem here. EINVAL is possible from other
|
|
* readdir implementations, but only if the result buffer is too small.
|
|
* since we control the size of that buffer, we should never have
|
|
* that problem.
|
|
*/
|
|
if (ret == EINVAL) {
|
|
ret = ENOENT;
|
|
}
|
|
#else
|
|
/* We're about to call a non-thread-safe readdir() that may
|
|
possibly set `errno', and the logic below actually cares about
|
|
errno after the call. Therefore we need to clear errno first. */
|
|
errno = 0;
|
|
thedir->entry = readdir(thedir->dirstruct);
|
|
if (thedir->entry == NULL) {
|
|
/* If NULL was returned, this can NEVER be a success. Can it?! */
|
|
if (errno == APR_SUCCESS) {
|
|
ret = APR_ENOENT;
|
|
}
|
|
else
|
|
ret = errno;
|
|
}
|
|
#endif
|
|
|
|
/* No valid bit flag to test here - do we want one? */
|
|
finfo->fname = NULL;
|
|
|
|
if (ret) {
|
|
finfo->valid = 0;
|
|
return ret;
|
|
}
|
|
|
|
#ifdef DIRENT_TYPE
|
|
type = filetype_from_dirent_type(thedir->entry->DIRENT_TYPE);
|
|
if (type != APR_UNKFILE) {
|
|
wanted &= ~APR_FINFO_TYPE;
|
|
}
|
|
#endif
|
|
#ifdef DIRENT_INODE
|
|
if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
|
|
wanted &= ~APR_FINFO_INODE;
|
|
}
|
|
#endif
|
|
|
|
wanted &= ~APR_FINFO_NAME;
|
|
|
|
if (wanted)
|
|
{
|
|
char fspec[APR_PATH_MAX];
|
|
int off;
|
|
apr_cpystrn(fspec, thedir->dirname, sizeof(fspec));
|
|
off = strlen(fspec);
|
|
if ((fspec[off - 1] != '/') && (off + 1 < sizeof(fspec)))
|
|
fspec[off++] = '/';
|
|
apr_cpystrn(fspec + off, thedir->entry->d_name, sizeof(fspec) - off);
|
|
ret = apr_stat(finfo, fspec, APR_FINFO_LINK | wanted, thedir->pool);
|
|
/* We passed a stack name that will disappear */
|
|
finfo->fname = NULL;
|
|
}
|
|
|
|
if (wanted && (ret == APR_SUCCESS || ret == APR_INCOMPLETE)) {
|
|
wanted &= ~finfo->valid;
|
|
}
|
|
else {
|
|
/* We don't bail because we fail to stat, when we are only -required-
|
|
* to readdir... but the result will be APR_INCOMPLETE
|
|
*/
|
|
finfo->pool = thedir->pool;
|
|
finfo->valid = 0;
|
|
#ifdef DIRENT_TYPE
|
|
if (type != APR_UNKFILE) {
|
|
finfo->filetype = type;
|
|
finfo->valid |= APR_FINFO_TYPE;
|
|
}
|
|
#endif
|
|
#ifdef DIRENT_INODE
|
|
if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
|
|
finfo->inode = thedir->entry->DIRENT_INODE;
|
|
finfo->valid |= APR_FINFO_INODE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
finfo->name = apr_pstrdup(thedir->pool, thedir->entry->d_name);
|
|
finfo->valid |= APR_FINFO_NAME;
|
|
|
|
if (wanted)
|
|
return APR_INCOMPLETE;
|
|
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
apr_status_t apr_dir_rewind(apr_dir_t *thedir)
|
|
{
|
|
rewinddir(thedir->dirstruct);
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
apr_status_t apr_dir_make(const char *path, apr_fileperms_t perm,
|
|
apr_pool_t *pool)
|
|
{
|
|
mode_t mode = apr_unix_perms2mode(perm);
|
|
|
|
if (mkdir(path, mode) == 0) {
|
|
return APR_SUCCESS;
|
|
}
|
|
else {
|
|
return errno;
|
|
}
|
|
}
|
|
|
|
apr_status_t apr_dir_make_recursive(const char *path, apr_fileperms_t perm,
|
|
apr_pool_t *pool)
|
|
{
|
|
apr_status_t apr_err = 0;
|
|
|
|
apr_err = apr_dir_make (path, perm, pool); /* Try to make PATH right out */
|
|
|
|
if (apr_err == EEXIST) /* It's OK if PATH exists */
|
|
return APR_SUCCESS;
|
|
|
|
if (apr_err == ENOENT) { /* Missing an intermediate dir */
|
|
char *dir;
|
|
|
|
dir = path_remove_last_component(path, pool);
|
|
/* If there is no path left, give up. */
|
|
if (dir[0] == '\0') {
|
|
return apr_err;
|
|
}
|
|
|
|
apr_err = apr_dir_make_recursive(dir, perm, pool);
|
|
|
|
if (!apr_err)
|
|
apr_err = apr_dir_make (path, perm, pool);
|
|
}
|
|
|
|
return apr_err;
|
|
}
|
|
|
|
apr_status_t apr_dir_remove(const char *path, apr_pool_t *pool)
|
|
{
|
|
if (rmdir(path) == 0) {
|
|
return APR_SUCCESS;
|
|
}
|
|
else {
|
|
return errno;
|
|
}
|
|
}
|
|
|
|
apr_status_t apr_os_dir_get(apr_os_dir_t **thedir, apr_dir_t *dir)
|
|
{
|
|
if (dir == NULL) {
|
|
return APR_ENODIR;
|
|
}
|
|
*thedir = dir->dirstruct;
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
apr_status_t apr_os_dir_put(apr_dir_t **dir, apr_os_dir_t *thedir,
|
|
apr_pool_t *pool)
|
|
{
|
|
if ((*dir) == NULL) {
|
|
(*dir) = (apr_dir_t *)apr_pcalloc(pool, sizeof(apr_dir_t));
|
|
(*dir)->pool = pool;
|
|
}
|
|
(*dir)->dirstruct = thedir;
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
|