1160814Ssimon/* Licensed to the Apache Software Foundation (ASF) under one or more 2160814Ssimon * contributor license agreements. See the NOTICE file distributed with 3160814Ssimon * this work for additional information regarding copyright ownership. 4160814Ssimon * The ASF licenses this file to You under the Apache License, Version 2.0 5160814Ssimon * (the "License"); you may not use this file except in compliance with 6160814Ssimon * the License. You may obtain a copy of the License at 7160814Ssimon * 8160814Ssimon * http://www.apache.org/licenses/LICENSE-2.0 9160814Ssimon * 10160814Ssimon * Unless required by applicable law or agreed to in writing, software 11160814Ssimon * distributed under the License is distributed on an "AS IS" BASIS, 12160814Ssimon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13160814Ssimon * See the License for the specific language governing permissions and 14160814Ssimon * limitations under the License. 15160814Ssimon */ 16160814Ssimon 17160814Ssimon#include "apr_arch_file_io.h" 18160814Ssimon#include "apr_strings.h" 19160814Ssimon#include "apr_portable.h" 20160814Ssimon#if APR_HAVE_SYS_SYSLIMITS_H 21160814Ssimon#include <sys/syslimits.h> 22160814Ssimon#endif 23160814Ssimon#if APR_HAVE_LIMITS_H 24160814Ssimon#include <limits.h> 25238405Sjkim#endif 26160814Ssimon 27160814Ssimonstatic apr_status_t dir_cleanup(void *thedir) 28160814Ssimon{ 29160814Ssimon apr_dir_t *dir = thedir; 30160814Ssimon if (closedir(dir->dirstruct) == 0) { 31160814Ssimon return APR_SUCCESS; 32160814Ssimon } 33160814Ssimon else { 34160814Ssimon return errno; 35160814Ssimon } 36238405Sjkim} 37160814Ssimon 38160814Ssimon#define PATH_SEPARATOR '/' 39160814Ssimon 40160814Ssimon/* Remove trailing separators that don't affect the meaning of PATH. */ 41160814Ssimonstatic const char *path_canonicalize (const char *path, apr_pool_t *pool) 42160814Ssimon{ 43160814Ssimon /* At some point this could eliminate redundant components. For 44160814Ssimon * now, it just makes sure there is no trailing slash. */ 45160814Ssimon apr_size_t len = strlen (path); 46160814Ssimon apr_size_t orig_len = len; 47160814Ssimon 48160814Ssimon while ((len > 0) && (path[len - 1] == PATH_SEPARATOR)) 49160814Ssimon len--; 50160814Ssimon 51160814Ssimon if (len != orig_len) 52160814Ssimon return apr_pstrndup (pool, path, len); 53160814Ssimon else 54160814Ssimon return path; 55160814Ssimon} 56160814Ssimon 57160814Ssimon/* Remove one component off the end of PATH. */ 58160814Ssimonstatic char *path_remove_last_component (const char *path, apr_pool_t *pool) 59160814Ssimon{ 60160814Ssimon const char *newpath = path_canonicalize (path, pool); 61160814Ssimon int i; 62160814Ssimon 63160814Ssimon for (i = (strlen(newpath) - 1); i >= 0; i--) { 64296341Sdelphij if (path[i] == PATH_SEPARATOR) 65296341Sdelphij break; 66160814Ssimon } 67160814Ssimon 68160814Ssimon return apr_pstrndup (pool, path, (i < 0) ? 0 : i); 69160814Ssimon} 70160814Ssimon 71160814Ssimonapr_status_t apr_dir_open(apr_dir_t **new, const char *dirname, 72160814Ssimon apr_pool_t *pool) 73160814Ssimon{ 74160814Ssimon /* On some platforms (e.g., Linux+GNU libc), d_name[] in struct 75160814Ssimon * dirent is declared with enough storage for the name. On other 76160814Ssimon * platforms (e.g., Solaris 8 for Intel), d_name is declared as a 77160814Ssimon * one-byte array. Note: gcc evaluates this at compile time. 78160814Ssimon */ 79160814Ssimon apr_size_t dirent_size = 80160814Ssimon sizeof(*(*new)->entry) + 81160814Ssimon (sizeof((*new)->entry->d_name) > 1 ? 0 : 255); 82160814Ssimon DIR *dir = opendir(dirname); 83160814Ssimon 84238405Sjkim if (!dir) { 85238405Sjkim 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