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
dir_cleanup(void * thedir)27 static 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. */
path_canonicalize(const char * path,apr_pool_t * pool)41 static 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. */
path_remove_last_component(const char * path,apr_pool_t * pool)58 static 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
apr_dir_open(apr_dir_t ** new,const char * dirname,apr_pool_t * pool)71 apr_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
apr_dir_close(apr_dir_t * thedir)100 apr_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
filetype_from_dirent_type(int type)106 static 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
apr_dir_read(apr_finfo_t * finfo,apr_int32_t wanted,apr_dir_t * thedir)133 apr_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
apr_dir_rewind(apr_dir_t * thedir)282 apr_status_t apr_dir_rewind(apr_dir_t *thedir)
283 {
284 rewinddir(thedir->dirstruct);
285 return APR_SUCCESS;
286 }
287
apr_dir_make(const char * path,apr_fileperms_t perm,apr_pool_t * pool)288 apr_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
apr_dir_make_recursive(const char * path,apr_fileperms_t perm,apr_pool_t * pool)301 apr_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
apr_dir_remove(const char * path,apr_pool_t * pool)334 apr_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
apr_os_dir_get(apr_os_dir_t ** thedir,apr_dir_t * dir)344 apr_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
apr_os_dir_put(apr_dir_t ** dir,apr_os_dir_t * thedir,apr_pool_t * pool)353 apr_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