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