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