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