1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "apr_arch_file_io.h" 18#include "apr_strings.h" 19#include "apr_portable.h" 20#if APR_HAVE_SYS_SYSLIMITS_H 21#include <sys/syslimits.h> 22#endif 23#if APR_HAVE_LIMITS_H 24#include <limits.h> 25#endif 26 27#ifndef NAME_MAX 28#define NAME_MAX 255 29#endif 30 31static apr_status_t dir_cleanup(void *thedir) 32{ 33 apr_dir_t *dir = thedir; 34 if (closedir(dir->dirstruct) == 0) { 35 return APR_SUCCESS; 36 } 37 else { 38 return errno; 39 } 40} 41 42#define PATH_SEPARATOR '/' 43 44/* Remove trailing separators that don't affect the meaning of PATH. */ 45static const char *path_canonicalize (const char *path, apr_pool_t *pool) 46{ 47 /* At some point this could eliminate redundant components. For 48 * now, it just makes sure there is no trailing slash. */ 49 apr_size_t len = strlen (path); 50 apr_size_t orig_len = len; 51 52 while ((len > 0) && (path[len - 1] == PATH_SEPARATOR)) 53 len--; 54 55 if (len != orig_len) 56 return apr_pstrndup (pool, path, len); 57 else 58 return path; 59} 60 61/* Remove one component off the end of PATH. */ 62static char *path_remove_last_component (const char *path, apr_pool_t *pool) 63{ 64 const char *newpath = path_canonicalize (path, pool); 65 int i; 66 67 for (i = (strlen(newpath) - 1); i >= 0; i--) { 68 if (path[i] == PATH_SEPARATOR) 69 break; 70 } 71 72 return apr_pstrndup (pool, path, (i < 0) ? 0 : i); 73} 74 75apr_status_t apr_dir_open(apr_dir_t **new, const char *dirname, 76 apr_pool_t *pool) 77{ 78 DIR *dir = opendir(dirname); 79 80 if (!dir) { 81 return errno; 82 } 83 84 (*new) = (apr_dir_t *)apr_palloc(pool, sizeof(apr_dir_t)); 85 86 (*new)->pool = pool; 87 (*new)->dirname = apr_pstrdup(pool, dirname); 88 (*new)->dirstruct = dir; 89 90#if APR_HAS_THREADS && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \ 91 && !defined(READDIR_IS_THREAD_SAFE) 92 /* On some platforms (e.g., Linux+GNU libc), d_name[] in struct 93 * dirent is declared with enough storage for the name. On other 94 * platforms (e.g., Solaris 8 for Intel), d_name is declared as a 95 * one-byte array. Note: gcc evaluates this at compile time. 96 */ 97 (*new)->entry = apr_pcalloc(pool, sizeof(*(*new)->entry) + 98 (sizeof((*new)->entry->d_name) > 1 99 ? 0 : NAME_MAX)); 100#else 101 (*new)->entry = NULL; 102#endif 103 104 apr_pool_cleanup_register((*new)->pool, *new, dir_cleanup, 105 apr_pool_cleanup_null); 106 return APR_SUCCESS; 107} 108 109apr_status_t apr_dir_close(apr_dir_t *thedir) 110{ 111 return apr_pool_cleanup_run(thedir->pool, thedir, dir_cleanup); 112} 113 114#ifdef DIRENT_TYPE 115static apr_filetype_e filetype_from_dirent_type(int type) 116{ 117 switch (type) { 118 case DT_REG: 119 return APR_REG; 120 case DT_DIR: 121 return APR_DIR; 122 case DT_LNK: 123 return APR_LNK; 124 case DT_CHR: 125 return APR_CHR; 126 case DT_BLK: 127 return APR_BLK; 128#if defined(DT_FIFO) 129 case DT_FIFO: 130 return APR_PIPE; 131#endif 132#if !defined(BEOS) && defined(DT_SOCK) 133 case DT_SOCK: 134 return APR_SOCK; 135#endif 136 default: 137 return APR_UNKFILE; 138 } 139} 140#endif 141 142apr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted, 143 apr_dir_t *thedir) 144{ 145 apr_status_t ret = 0; 146#ifdef DIRENT_TYPE 147 apr_filetype_e type; 148#endif 149#if APR_HAS_THREADS && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \ 150 && !defined(READDIR_IS_THREAD_SAFE) 151#ifdef APR_USE_READDIR64_R 152 struct dirent64 *retent; 153 154 /* If LFS is enabled and readdir64_r is available, readdir64_r is 155 * used in preference to readdir_r. This allows directories to be 156 * read which contain a (64-bit) inode number which doesn't fit 157 * into the 32-bit apr_ino_t, iff the caller doesn't actually care 158 * about the inode number (i.e. wanted & APR_FINFO_INODE == 0). 159 * (such inodes may be seen in some wonky NFS environments) 160 * 161 * Similarly, if the d_off field cannot be reprented in a 32-bit 162 * offset, the libc readdir_r() would barf; using readdir64_r 163 * bypasses that case entirely since APR does not care about 164 * d_off. */ 165 166 ret = readdir64_r(thedir->dirstruct, thedir->entry, &retent); 167#else 168 169 struct dirent *retent; 170 171 ret = readdir_r(thedir->dirstruct, thedir->entry, &retent); 172#endif 173 174 /* POSIX treats "end of directory" as a non-error case, so ret 175 * will be zero and retent will be set to NULL in that case. */ 176 if (!ret && retent == NULL) { 177 ret = APR_ENOENT; 178 } 179 180 /* Solaris is a bit strange, if there are no more entries in the 181 * directory, it returns EINVAL. Since this is against POSIX, we 182 * hack around the problem here. EINVAL is possible from other 183 * readdir implementations, but only if the result buffer is too small. 184 * since we control the size of that buffer, we should never have 185 * that problem. 186 */ 187 if (ret == EINVAL) { 188 ret = APR_ENOENT; 189 } 190#else 191 /* We're about to call a non-thread-safe readdir() that may 192 possibly set `errno', and the logic below actually cares about 193 errno after the call. Therefore we need to clear errno first. */ 194 errno = 0; 195 thedir->entry = readdir(thedir->dirstruct); 196 if (thedir->entry == NULL) { 197 /* If NULL was returned, this can NEVER be a success. Can it?! */ 198 if (errno == APR_SUCCESS) { 199 ret = APR_ENOENT; 200 } 201 else 202 ret = errno; 203 } 204#endif 205 206 /* No valid bit flag to test here - do we want one? */ 207 finfo->fname = NULL; 208 209 if (ret) { 210 finfo->valid = 0; 211 return ret; 212 } 213 214#ifdef DIRENT_TYPE 215 type = filetype_from_dirent_type(thedir->entry->DIRENT_TYPE); 216 if (type != APR_UNKFILE) { 217 wanted &= ~APR_FINFO_TYPE; 218 } 219#endif 220#ifdef DIRENT_INODE 221 if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) { 222#ifdef APR_USE_READDIR64_R 223 /* If readdir64_r is used, check for the overflow case of trying 224 * to fit a 64-bit integer into a 32-bit integer. */ 225 if (sizeof(apr_ino_t) >= sizeof(retent->DIRENT_INODE) 226 || (apr_ino_t)retent->DIRENT_INODE == retent->DIRENT_INODE) { 227 wanted &= ~APR_FINFO_INODE; 228 } else { 229 /* Prevent the fallback code below from filling in the 230 * inode if the stat call fails. */ 231 retent->DIRENT_INODE = 0; 232 } 233#else 234 wanted &= ~APR_FINFO_INODE; 235#endif /* APR_USE_READDIR64_R */ 236 } 237#endif /* DIRENT_INODE */ 238 239 wanted &= ~APR_FINFO_NAME; 240 241 if (wanted) 242 { 243 char fspec[APR_PATH_MAX]; 244 char *end; 245 246 end = apr_cpystrn(fspec, thedir->dirname, sizeof fspec); 247 248 if (end > fspec && end[-1] != '/' && (end < fspec + APR_PATH_MAX)) 249 *end++ = '/'; 250 251 apr_cpystrn(end, thedir->entry->d_name, 252 sizeof fspec - (end - fspec)); 253 254 ret = apr_stat(finfo, fspec, APR_FINFO_LINK | wanted, thedir->pool); 255 /* We passed a stack name that will disappear */ 256 finfo->fname = NULL; 257 } 258 259 if (wanted && (ret == APR_SUCCESS || ret == APR_INCOMPLETE)) { 260 wanted &= ~finfo->valid; 261 } 262 else { 263 /* We don't bail because we fail to stat, when we are only -required- 264 * to readdir... but the result will be APR_INCOMPLETE 265 */ 266 finfo->pool = thedir->pool; 267 finfo->valid = 0; 268#ifdef DIRENT_TYPE 269 if (type != APR_UNKFILE) { 270 finfo->filetype = type; 271 finfo->valid |= APR_FINFO_TYPE; 272 } 273#endif 274#ifdef DIRENT_INODE 275 if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) { 276 finfo->inode = thedir->entry->DIRENT_INODE; 277 finfo->valid |= APR_FINFO_INODE; 278 } 279#endif 280 } 281 282 finfo->name = apr_pstrdup(thedir->pool, thedir->entry->d_name); 283 finfo->valid |= APR_FINFO_NAME; 284 285 if (wanted) 286 return APR_INCOMPLETE; 287 288 return APR_SUCCESS; 289} 290 291apr_status_t apr_dir_rewind(apr_dir_t *thedir) 292{ 293 rewinddir(thedir->dirstruct); 294 return APR_SUCCESS; 295} 296 297apr_status_t apr_dir_make(const char *path, apr_fileperms_t perm, 298 apr_pool_t *pool) 299{ 300 mode_t mode = apr_unix_perms2mode(perm); 301 302 if (mkdir(path, mode) == 0) { 303 return APR_SUCCESS; 304 } 305 else { 306 return errno; 307 } 308} 309 310apr_status_t apr_dir_make_recursive(const char *path, apr_fileperms_t perm, 311 apr_pool_t *pool) 312{ 313 apr_status_t apr_err = 0; 314 315 apr_err = apr_dir_make (path, perm, pool); /* Try to make PATH right out */ 316 317 if (apr_err == ENOENT) { /* Missing an intermediate dir */ 318 char *dir; 319 320 dir = path_remove_last_component(path, pool); 321 /* If there is no path left, give up. */ 322 if (dir[0] == '\0') { 323 return apr_err; 324 } 325 326 apr_err = apr_dir_make_recursive(dir, perm, pool); 327 328 if (!apr_err) 329 apr_err = apr_dir_make (path, perm, pool); 330 } 331 332 /* 333 * It's OK if PATH exists. Timing issues can lead to the second 334 * apr_dir_make being called on existing dir, therefore this check 335 * has to come last. 336 */ 337 if (APR_STATUS_IS_EEXIST(apr_err)) 338 return APR_SUCCESS; 339 340 return apr_err; 341} 342 343apr_status_t apr_dir_remove(const char *path, apr_pool_t *pool) 344{ 345 if (rmdir(path) == 0) { 346 return APR_SUCCESS; 347 } 348 else { 349 return errno; 350 } 351} 352 353apr_status_t apr_os_dir_get(apr_os_dir_t **thedir, apr_dir_t *dir) 354{ 355 if (dir == NULL) { 356 return APR_ENODIR; 357 } 358 *thedir = dir->dirstruct; 359 return APR_SUCCESS; 360} 361 362apr_status_t apr_os_dir_put(apr_dir_t **dir, apr_os_dir_t *thedir, 363 apr_pool_t *pool) 364{ 365 if ((*dir) == NULL) { 366 (*dir) = (apr_dir_t *)apr_pcalloc(pool, sizeof(apr_dir_t)); 367 (*dir)->pool = pool; 368 } 369 (*dir)->dirstruct = thedir; 370 return APR_SUCCESS; 371} 372 373 374