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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
27 static apr_status_t dir_cleanup(void *thedir)
29 apr_dir_t *dir = thedir;
30 if (closedir(dir->dirstruct) == 0) {
38 #define PATH_SEPARATOR '/'
40 /* Remove trailing separators that don't affect the meaning of PATH. */
41 static const char *path_canonicalize (const char *path, apr_pool_t *pool)
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;
48 while ((len > 0) && (path[len - 1] == PATH_SEPARATOR))
52 return apr_pstrndup (pool, path, len);
57 /* Remove one component off the end of PATH. */
58 static char *path_remove_last_component (const char *path, apr_pool_t *pool)
60 const char *newpath = path_canonicalize (path, pool);
63 for (i = (strlen(newpath) - 1); i >= 0; i--) {
64 if (path[i] == PATH_SEPARATOR)
68 return apr_pstrndup (pool, path, (i < 0) ? 0 : i);
71 apr_status_t apr_dir_open(apr_dir_t **new, const char *dirname,
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.
79 apr_size_t dirent_size =
80 (sizeof((*new)->entry->d_name) > 1 ?
81 sizeof(struct dirent) : sizeof (struct dirent) + 255);
83 (*new) = (apr_dir_t *)apr_palloc(pool, sizeof(apr_dir_t));
86 (*new)->dirname = apr_pstrdup(pool, dirname);
87 (*new)->dirstruct = opendir(dirname);
88 (*new)->entry = apr_pcalloc(pool, dirent_size);
90 if ((*new)->dirstruct == NULL) {
94 apr_pool_cleanup_register((*new)->pool, (void *)(*new), dir_cleanup,
95 apr_pool_cleanup_null);
100 apr_status_t apr_dir_close(apr_dir_t *thedir)
102 return apr_pool_cleanup_run(thedir->pool, thedir, dir_cleanup);
106 static apr_filetype_e filetype_from_dirent_type(int type)
123 #if !defined(BEOS) && defined(DT_SOCK)
133 apr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted,
136 apr_status_t ret = 0;
140 #if APR_HAS_THREADS && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \
141 && !defined(READDIR_IS_THREAD_SAFE)
142 struct dirent *retent;
144 ret = readdir_r(thedir->dirstruct, thedir->entry, &retent);
146 /* Avoid the Linux problem where at end-of-directory thedir->entry
147 * is set to NULL, but ret = APR_SUCCESS.
149 if(!ret && thedir->entry != retent)
152 /* Solaris is a bit strange, if there are no more entries in the
153 * directory, it returns EINVAL. Since this is against POSIX, we
154 * hack around the problem here. EINVAL is possible from other
155 * readdir implementations, but only if the result buffer is too small.
156 * since we control the size of that buffer, we should never have
163 /* We're about to call a non-thread-safe readdir() that may
164 possibly set `errno', and the logic below actually cares about
165 errno after the call. Therefore we need to clear errno first. */
167 thedir->entry = readdir(thedir->dirstruct);
168 if (thedir->entry == NULL) {
169 /* If NULL was returned, this can NEVER be a success. Can it?! */
170 if (errno == APR_SUCCESS) {
178 /* No valid bit flag to test here - do we want one? */
187 type = filetype_from_dirent_type(thedir->entry->DIRENT_TYPE);
188 if (type != APR_UNKFILE) {
189 wanted &= ~APR_FINFO_TYPE;
193 if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
194 wanted &= ~APR_FINFO_INODE;
198 wanted &= ~APR_FINFO_NAME;
202 char fspec[APR_PATH_MAX];
204 apr_cpystrn(fspec, thedir->dirname, sizeof(fspec));
206 if ((fspec[off - 1] != '/') && (off + 1 < sizeof(fspec)))
208 apr_cpystrn(fspec + off, thedir->entry->d_name, sizeof(fspec) - off);
209 ret = apr_lstat(finfo, fspec, wanted, thedir->pool);
210 /* We passed a stack name that will disappear */
214 if (wanted && (ret == APR_SUCCESS || ret == APR_INCOMPLETE)) {
215 wanted &= ~finfo->valid;
218 /* We don't bail because we fail to stat, when we are only -required-
219 * to readdir... but the result will be APR_INCOMPLETE
221 finfo->pool = thedir->pool;
224 if (type != APR_UNKFILE) {
225 finfo->filetype = type;
226 finfo->valid |= APR_FINFO_TYPE;
230 if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
231 finfo->inode = thedir->entry->DIRENT_INODE;
232 finfo->valid |= APR_FINFO_INODE;
237 finfo->name = apr_pstrdup(thedir->pool, thedir->entry->d_name);
238 finfo->valid |= APR_FINFO_NAME;
241 return APR_INCOMPLETE;
246 apr_status_t apr_dir_rewind(apr_dir_t *thedir)
248 rewinddir(thedir->dirstruct);
252 apr_status_t apr_dir_make(const char *path, apr_fileperms_t perm,
255 mode_t mode = apr_unix_perms2mode(perm);
257 if (mkdir(path, mode) == 0) {
265 apr_status_t apr_dir_make_recursive(const char *path, apr_fileperms_t perm,
268 apr_status_t apr_err = 0;
270 apr_err = apr_dir_make (path, perm, pool); /* Try to make PATH right out */
272 if (apr_err == EEXIST) /* It's OK if PATH exists */
275 if (apr_err == ENOENT) { /* Missing an intermediate dir */
278 dir = path_remove_last_component(path, pool);
279 /* If there is no path left, give up. */
280 if (dir[0] == '\0') {
284 apr_err = apr_dir_make_recursive(dir, perm, pool);
287 apr_err = apr_dir_make (path, perm, pool);
293 apr_status_t apr_dir_remove(const char *path, apr_pool_t *pool)
295 if (rmdir(path) == 0) {
303 apr_status_t apr_os_dir_get(apr_os_dir_t **thedir, apr_dir_t *dir)
308 *thedir = dir->dirstruct;
312 apr_status_t apr_os_dir_put(apr_dir_t **dir, apr_os_dir_t *thedir,
315 if ((*dir) == NULL) {
316 (*dir) = (apr_dir_t *)apr_pcalloc(pool, sizeof(apr_dir_t));
319 (*dir)->dirstruct = thedir;