]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/apr/file_io/unix/dir.c
Fix completion descriptors alignment for the ENA
[FreeBSD/FreeBSD.git] / contrib / apr / file_io / unix / dir.c
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
31 static 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. */
45 static 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. */
62 static 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
75 apr_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
109 apr_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
115 static 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
142 apr_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
291 apr_status_t apr_dir_rewind(apr_dir_t *thedir)
292 {
293     rewinddir(thedir->dirstruct);
294     return APR_SUCCESS;
295 }
296
297 apr_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
310 apr_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
343 apr_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
353 apr_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
362 apr_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