#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 '/'
static const char *path_canonicalize (const char *path, apr_pool_t *pool)
{
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;
}
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)
{
apr_size_t dirent_size =
sizeof(*(*new)->entry) +
(sizeof((*new)->entry->d_name) > 1 ? 0 : 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)
#ifdef APR_USE_READDIR64_R
struct dirent64 *retent;
ret = readdir64_r(thedir->dirstruct, thedir->entry, &retent);
#else
struct dirent *retent;
ret = readdir_r(thedir->dirstruct, thedir->entry, &retent);
#endif
if (!ret && retent == NULL) {
ret = APR_ENOENT;
}
if (ret == EINVAL) {
ret = APR_ENOENT;
}
#else
errno = 0;
thedir->entry = readdir(thedir->dirstruct);
if (thedir->entry == NULL) {
if (errno == APR_SUCCESS) {
ret = APR_ENOENT;
}
else
ret = errno;
}
#endif
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) {
#ifdef APR_USE_READDIR64_R
if (sizeof(apr_ino_t) >= sizeof(retent->DIRENT_INODE)
|| (apr_ino_t)retent->DIRENT_INODE == retent->DIRENT_INODE) {
wanted &= ~APR_FINFO_INODE;
} else {
retent->DIRENT_INODE = 0;
}
#else
wanted &= ~APR_FINFO_INODE;
#endif
}
#endif
wanted &= ~APR_FINFO_NAME;
if (wanted)
{
char fspec[APR_PATH_MAX];
char *end;
end = apr_cpystrn(fspec, thedir->dirname, sizeof fspec);
if (end > fspec && end[-1] != '/' && (end < fspec + APR_PATH_MAX))
*end++ = '/';
apr_cpystrn(end, thedir->entry->d_name,
sizeof fspec - (end - fspec));
ret = apr_stat(finfo, fspec, APR_FINFO_LINK | wanted, thedir->pool);
finfo->fname = NULL;
}
if (wanted && (ret == APR_SUCCESS || ret == APR_INCOMPLETE)) {
wanted &= ~finfo->valid;
}
else {
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);
if (apr_err == EEXIST)
return APR_SUCCESS;
if (apr_err == ENOENT) {
char *dir;
dir = path_remove_last_component(path, pool);
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;
}