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"
19 #include "nks/dirio.h"
20 #include "apr_file_io.h"
21 #include "apr_general.h"
22 #include "apr_strings.h"
23 #include "apr_errno.h"
25 #include "apr_thread_rwlock.h"
33 static apr_filetype_e filetype_from_mode(mode_t mode)
35 apr_filetype_e type = APR_NOFILE;
39 else if (S_ISDIR(mode))
41 else if (S_ISCHR(mode))
43 else if (S_ISBLK(mode))
45 else if (S_ISFIFO(mode))
47 else if (S_ISLNK(mode))
49 else if (S_ISSOCK(mode))
56 static void fill_out_finfo(apr_finfo_t *finfo, struct stat *info,
59 finfo->valid = APR_FINFO_MIN | APR_FINFO_IDENT | APR_FINFO_NLINK
60 | APR_FINFO_OWNER | APR_FINFO_PROT;
61 finfo->protection = apr_unix_mode2perms(info->st_mode);
62 finfo->filetype = filetype_from_mode(info->st_mode);
63 finfo->user = info->st_uid;
64 finfo->group = info->st_gid;
65 finfo->size = info->st_size;
66 finfo->inode = info->st_ino;
67 finfo->device = info->st_dev;
68 finfo->nlink = info->st_nlink;
69 apr_time_ansi_put(&finfo->atime, info->st_atime.tv_sec);
70 apr_time_ansi_put(&finfo->mtime, info->st_mtime.tv_sec);
71 apr_time_ansi_put(&finfo->ctime, info->st_ctime.tv_sec);
72 /* ### needs to be revisited
73 * if (wanted & APR_FINFO_CSIZE) {
74 * finfo->csize = info->st_blocks * 512;
75 * finfo->valid |= APR_FINFO_CSIZE;
80 apr_status_t apr_file_info_get_locked(apr_finfo_t *finfo,
86 if (thefile->buffered) {
87 apr_status_t rv = apr_file_flush_locked(thefile);
88 if (rv != APR_SUCCESS)
92 if (fstat(thefile->filedes, &info) == 0) {
93 finfo->pool = thefile->pool;
94 finfo->fname = thefile->fname;
95 fill_out_finfo(finfo, &info, wanted);
96 return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
103 APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo,
109 if (thefile->buffered) {
110 apr_status_t rv = apr_file_flush(thefile);
111 if (rv != APR_SUCCESS)
115 if (fstat(thefile->filedes, &info) == 0) {
116 finfo->pool = thefile->pool;
117 finfo->fname = thefile->fname;
118 fill_out_finfo(finfo, &info, wanted);
119 return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
126 APR_DECLARE(apr_status_t) apr_file_perms_set(const char *fname,
127 apr_fileperms_t perms)
129 mode_t mode = apr_unix_perms2mode(perms);
131 if (chmod(fname, mode) == -1)
136 APR_DECLARE(apr_status_t) apr_file_attrs_set(const char *fname,
137 apr_fileattrs_t attributes,
138 apr_fileattrs_t attr_mask,
144 /* Don't do anything if we can't handle the requested attributes */
145 if (!(attr_mask & (APR_FILE_ATTR_READONLY
146 | APR_FILE_ATTR_EXECUTABLE)))
149 status = apr_stat(&finfo, fname, APR_FINFO_PROT, pool);
150 if (!APR_STATUS_IS_SUCCESS(status))
153 /* ### TODO: should added bits be umask'd? */
154 if (attr_mask & APR_FILE_ATTR_READONLY)
156 if (attributes & APR_FILE_ATTR_READONLY)
158 finfo.protection &= ~APR_UWRITE;
159 finfo.protection &= ~APR_GWRITE;
160 finfo.protection &= ~APR_WWRITE;
164 /* ### umask this! */
165 finfo.protection |= APR_UWRITE;
166 finfo.protection |= APR_GWRITE;
167 finfo.protection |= APR_WWRITE;
171 if (attr_mask & APR_FILE_ATTR_EXECUTABLE)
173 if (attributes & APR_FILE_ATTR_EXECUTABLE)
175 /* ### umask this! */
176 finfo.protection |= APR_UEXECUTE;
177 finfo.protection |= APR_GEXECUTE;
178 finfo.protection |= APR_WEXECUTE;
182 finfo.protection &= ~APR_UEXECUTE;
183 finfo.protection &= ~APR_GEXECUTE;
184 finfo.protection &= ~APR_WEXECUTE;
188 return apr_file_perms_set(fname, finfo.protection);
192 static apr_status_t stat_cache_cleanup(void *data)
194 apr_pool_t *p = (apr_pool_t *)getGlobalPool();
195 apr_hash_index_t *hi;
196 apr_hash_t *statCache = (apr_hash_t*)data;
201 for (hi = apr_hash_first(p, statCache); hi; hi = apr_hash_next(hi)) {
202 apr_hash_this(hi, (const void**)&key, &keylen, (void**)&pathctx);
205 NXFreePathContext(pathctx);
212 int cstat (NXPathCtx_t ctx, char *path, struct stat *buf, unsigned long requestmap, apr_pool_t *p)
214 apr_pool_t *gPool = (apr_pool_t *)getGlobalPool();
215 apr_hash_t *statCache = NULL;
216 apr_thread_rwlock_t *rwlock = NULL;
218 NXPathCtx_t pathctx = 0;
219 char *ptr = NULL, *tr;
226 /* If there isn't a global pool then just stat the file
231 if (apr_pool_create(&gPool, NULL) != APR_SUCCESS) {
232 return getstat(ctx, path, buf, requestmap);
235 setGlobalPool(gPool);
236 apr_pool_tag(gPool, apr_pstrdup(gPool, "cstat_mem_pool"));
238 statCache = apr_hash_make(gPool);
239 apr_pool_userdata_set ((void*)statCache, "STAT_CACHE", stat_cache_cleanup, gPool);
241 apr_thread_rwlock_create(&rwlock, gPool);
242 apr_pool_userdata_set ((void*)rwlock, "STAT_CACHE_LOCK", apr_pool_cleanup_null, gPool);
245 apr_pool_userdata_get((void**)&statCache, "STAT_CACHE", gPool);
246 apr_pool_userdata_get((void**)&rwlock, "STAT_CACHE_LOCK", gPool);
249 if (!gPool || !statCache || !rwlock) {
250 return getstat(ctx, path, buf, requestmap);
253 for (x = 0,tr = path;*tr != '\0';tr++,x++) {
254 if (*tr == '\\' || *tr == '/') {
265 ppath = apr_pstrndup (p, path, len);
267 if (ptr[1] != '\0') {
270 /* If the path ended in a trailing slash then our result path
271 will be a single slash. To avoid stat'ing the root with a
272 slash, we need to make sure we stat the current directory
274 if (((*ptr == '/') || (*ptr == '\\')) && (*(ptr+1) == '\0')) {
275 pinfo = apr_pstrdup (p, ".");
278 pinfo = apr_pstrdup (p, ptr);
282 /* If we have a statCache then try to pull the information
283 from the cache. Otherwise just stat the file and return.*/
285 apr_thread_rwlock_rdlock(rwlock);
286 pathctx = (NXPathCtx_t) apr_hash_get(statCache, ppath, APR_HASH_KEY_STRING);
287 apr_thread_rwlock_unlock(rwlock);
289 return getstat(pathctx, pinfo, buf, requestmap);
294 err = NXCreatePathContext(0, ppath, 0, NULL, &pathctx);
296 apr_thread_rwlock_wrlock(rwlock);
297 apr_hash_set(statCache, apr_pstrdup(gPool,ppath) , APR_HASH_KEY_STRING, (void*)pathctx);
298 apr_thread_rwlock_unlock(rwlock);
299 return getstat(pathctx, pinfo, buf, requestmap);
304 return getstat(ctx, path, buf, requestmap);
308 APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo,
310 apr_int32_t wanted, apr_pool_t *pool)
314 NXPathCtx_t pathCtx = 0;
316 getcwdpath(NULL, &pathCtx, CTX_ACTUAL_CWD);
318 srv = getstat(pathCtx, (char*)fname, &info, ST_STAT_BITS|ST_NAME_BIT);
320 srv = cstat(pathCtx, (char*)fname, &info, ST_STAT_BITS|ST_NAME_BIT, pool);
326 finfo->fname = fname;
327 fill_out_finfo(finfo, &info, wanted);
328 if (wanted & APR_FINFO_LINK)
329 wanted &= ~APR_FINFO_LINK;
330 if (wanted & APR_FINFO_NAME) {
331 finfo->name = apr_pstrdup(pool, info.st_name);
332 finfo->valid |= APR_FINFO_NAME;
334 return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
337 #if !defined(ENOENT) || !defined(ENOTDIR)
338 #error ENOENT || ENOTDIR not defined; please see the
339 #error comments at this line in the source for a workaround.
341 * If ENOENT || ENOTDIR is not defined in one of the your OS's
342 * include files, APR cannot report a good reason why the stat()
343 * of the file failed; there are cases where it can fail even though
344 * the file exists. This opens holes in Apache, for example, because
345 * it becomes possible for someone to get a directory listing of a
346 * directory even though there is an index (eg. index.html) file in
347 * it. If you do not have a problem with this, delete the above
348 * #error lines and start the compile again. If you need to do this,
349 * please submit a bug report to http://www.apache.org/bug_report.html
350 * letting us know that you needed to do this. Please be sure to
351 * include the operating system you are using.
353 /* WARNING: All errors will be handled as not found
358 /* WARNING: All errors but not found will be handled as not directory
365 #else /* All was defined well, report the usual: */
371 /* Perhaps this becomes nothing but a macro?
373 APR_DECLARE(apr_status_t) apr_lstat(apr_finfo_t *finfo, const char *fname,
374 apr_int32_t wanted, apr_pool_t *pool)
376 return apr_stat(finfo, fname, wanted | APR_FINFO_LINK, pool);
380 APR_DECLARE(apr_status_t) apr_file_mtime_set(const char *fname,
387 status = apr_stat(&finfo, fname, APR_FINFO_ATIME, pool);
388 if (!APR_STATUS_IS_SUCCESS(status)) {
394 struct timeval tvp[2];
396 tvp[0].tv_sec = apr_time_sec(finfo.atime);
397 tvp[0].tv_usec = apr_time_usec(finfo.atime);
398 tvp[1].tv_sec = apr_time_sec(mtime);
399 tvp[1].tv_usec = apr_time_usec(mtime);
401 if (utimes(fname, tvp) == -1) {
405 #elif defined(HAVE_UTIME)
409 buf.actime = (time_t) (finfo.atime / APR_USEC_PER_SEC);
410 buf.modtime = (time_t) (mtime / APR_USEC_PER_SEC);
412 if (utime(fname, &buf) == -1) {