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 "fsio.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" 24#include "apr_hash.h" 25#include "apr_thread_rwlock.h" 26 27#ifdef HAVE_UTIME_H 28#include <utime.h> 29#endif 30 31#define APR_HAS_PSA 32 33static apr_filetype_e filetype_from_mode(mode_t mode) 34{ 35 apr_filetype_e type = APR_NOFILE; 36 37 if (S_ISREG(mode)) 38 type = APR_REG; 39 else if (S_ISDIR(mode)) 40 type = APR_DIR; 41 else if (S_ISCHR(mode)) 42 type = APR_CHR; 43 else if (S_ISBLK(mode)) 44 type = APR_BLK; 45 else if (S_ISFIFO(mode)) 46 type = APR_PIPE; 47 else if (S_ISLNK(mode)) 48 type = APR_LNK; 49 else if (S_ISSOCK(mode)) 50 type = APR_SOCK; 51 else 52 type = APR_UNKFILE; 53 return type; 54} 55 56static void fill_out_finfo(apr_finfo_t *finfo, struct stat *info, 57 apr_int32_t wanted) 58{ 59 finfo->valid = APR_FINFO_MIN | APR_FINFO_IDENT | APR_FINFO_NLINK 60 | APR_FINFO_OWNER | APR_FINFO_PROT; 61 62 finfo->protection = apr_unix_mode2perms(info->st_mode); 63 finfo->filetype = filetype_from_mode(info->st_mode); 64 finfo->user = info->st_uid; 65 finfo->group = info->st_gid; 66 finfo->size = info->st_size; 67 finfo->inode = info->st_ino; 68 finfo->device = info->st_dev; 69 finfo->nlink = info->st_nlink; 70 71 apr_time_ansi_put(&finfo->atime, info->st_atime.tv_sec); 72 apr_time_ansi_put(&finfo->mtime, info->st_mtime.tv_sec); 73 apr_time_ansi_put(&finfo->ctime, info->st_ctime.tv_sec); 74 75#ifdef HAVE_STRUCT_STAT_ST_BLOCKS 76#ifdef DEV_BSIZE 77 finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)DEV_BSIZE; 78#else 79 finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)512; 80#endif 81 finfo->valid |= APR_FINFO_CSIZE; 82#endif 83} 84 85apr_status_t apr_file_info_get_locked(apr_finfo_t *finfo, apr_int32_t wanted, 86 apr_file_t *thefile) 87{ 88 struct_stat info; 89 90 if (thefile->buffered) { 91 apr_status_t rv = apr_file_flush_locked(thefile); 92 if (rv != APR_SUCCESS) 93 return rv; 94 } 95 96 if (fstat(thefile->filedes, &info) == 0) { 97 finfo->pool = thefile->pool; 98 finfo->fname = thefile->fname; 99 fill_out_finfo(finfo, &info, wanted); 100 return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS; 101 } 102 else { 103 return errno; 104 } 105} 106 107APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, 108 apr_int32_t wanted, 109 apr_file_t *thefile) 110{ 111 struct stat info; 112 113 if (thefile->buffered) { 114 /* XXX: flush here is not mutex protected */ 115 apr_status_t rv = apr_file_flush(thefile); 116 if (rv != APR_SUCCESS) 117 return rv; 118 } 119 120 if (fstat(thefile->filedes, &info) == 0) { 121 finfo->pool = thefile->pool; 122 finfo->fname = thefile->fname; 123 fill_out_finfo(finfo, &info, wanted); 124 return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS; 125 } 126 else { 127 return errno; 128 } 129} 130 131APR_DECLARE(apr_status_t) apr_file_perms_set(const char *fname, 132 apr_fileperms_t perms) 133{ 134 mode_t mode = apr_unix_perms2mode(perms); 135 136 if (chmod(fname, mode) == -1) 137 return errno; 138 return APR_SUCCESS; 139} 140 141APR_DECLARE(apr_status_t) apr_file_attrs_set(const char *fname, 142 apr_fileattrs_t attributes, 143 apr_fileattrs_t attr_mask, 144 apr_pool_t *pool) 145{ 146 apr_status_t status; 147 apr_finfo_t finfo; 148 149 /* Don't do anything if we can't handle the requested attributes */ 150 if (!(attr_mask & (APR_FILE_ATTR_READONLY 151 | APR_FILE_ATTR_EXECUTABLE))) 152 return APR_SUCCESS; 153 154 status = apr_stat(&finfo, fname, APR_FINFO_PROT, pool); 155 if (status) 156 return status; 157 158 /* ### TODO: should added bits be umask'd? */ 159 if (attr_mask & APR_FILE_ATTR_READONLY) 160 { 161 if (attributes & APR_FILE_ATTR_READONLY) 162 { 163 finfo.protection &= ~APR_UWRITE; 164 finfo.protection &= ~APR_GWRITE; 165 finfo.protection &= ~APR_WWRITE; 166 } 167 else 168 { 169 /* ### umask this! */ 170 finfo.protection |= APR_UWRITE; 171 finfo.protection |= APR_GWRITE; 172 finfo.protection |= APR_WWRITE; 173 } 174 } 175 176 if (attr_mask & APR_FILE_ATTR_EXECUTABLE) 177 { 178 if (attributes & APR_FILE_ATTR_EXECUTABLE) 179 { 180 /* ### umask this! */ 181 finfo.protection |= APR_UEXECUTE; 182 finfo.protection |= APR_GEXECUTE; 183 finfo.protection |= APR_WEXECUTE; 184 } 185 else 186 { 187 finfo.protection &= ~APR_UEXECUTE; 188 finfo.protection &= ~APR_GEXECUTE; 189 finfo.protection &= ~APR_WEXECUTE; 190 } 191 } 192 193 return apr_file_perms_set(fname, finfo.protection); 194} 195 196#ifndef APR_HAS_PSA 197static apr_status_t stat_cache_cleanup(void *data) 198{ 199 apr_pool_t *p = (apr_pool_t *)getGlobalPool(); 200 apr_hash_index_t *hi; 201 apr_hash_t *statCache = (apr_hash_t*)data; 202 char *key; 203 apr_ssize_t keylen; 204 NXPathCtx_t pathctx; 205 206 for (hi = apr_hash_first(p, statCache); hi; hi = apr_hash_next(hi)) { 207 apr_hash_this(hi, (const void**)&key, &keylen, (void**)&pathctx); 208 209 if (pathctx) { 210 NXFreePathContext(pathctx); 211 } 212 } 213 214 return APR_SUCCESS; 215} 216 217int cstat (NXPathCtx_t ctx, char *path, struct stat *buf, unsigned long requestmap, apr_pool_t *p) 218{ 219 apr_pool_t *gPool = (apr_pool_t *)getGlobalPool(); 220 apr_hash_t *statCache = NULL; 221 apr_thread_rwlock_t *rwlock = NULL; 222 223 NXPathCtx_t pathctx = 0; 224 char *ptr = NULL, *tr; 225 int len = 0, x; 226 char *ppath; 227 char *pinfo; 228 229 if (ctx == 1) { 230 231 /* If there isn't a global pool then just stat the file 232 and return */ 233 if (!gPool) { 234 char poolname[50]; 235 236 if (apr_pool_create(&gPool, NULL) != APR_SUCCESS) { 237 return getstat(ctx, path, buf, requestmap); 238 } 239 240 setGlobalPool(gPool); 241 apr_pool_tag(gPool, apr_pstrdup(gPool, "cstat_mem_pool")); 242 243 statCache = apr_hash_make(gPool); 244 apr_pool_userdata_set ((void*)statCache, "STAT_CACHE", stat_cache_cleanup, gPool); 245 246 apr_thread_rwlock_create(&rwlock, gPool); 247 apr_pool_userdata_set ((void*)rwlock, "STAT_CACHE_LOCK", apr_pool_cleanup_null, gPool); 248 } 249 else { 250 apr_pool_userdata_get((void**)&statCache, "STAT_CACHE", gPool); 251 apr_pool_userdata_get((void**)&rwlock, "STAT_CACHE_LOCK", gPool); 252 } 253 254 if (!gPool || !statCache || !rwlock) { 255 return getstat(ctx, path, buf, requestmap); 256 } 257 258 for (x = 0,tr = path;*tr != '\0';tr++,x++) { 259 if (*tr == '\\' || *tr == '/') { 260 ptr = tr; 261 len = x; 262 } 263 if (*tr == ':') { 264 ptr = "\\"; 265 len = x; 266 } 267 } 268 269 if (ptr) { 270 ppath = apr_pstrndup (p, path, len); 271 strlwr(ppath); 272 if (ptr[1] != '\0') { 273 ptr++; 274 } 275 /* If the path ended in a trailing slash then our result path 276 will be a single slash. To avoid stat'ing the root with a 277 slash, we need to make sure we stat the current directory 278 with a dot */ 279 if (((*ptr == '/') || (*ptr == '\\')) && (*(ptr+1) == '\0')) { 280 pinfo = apr_pstrdup (p, "."); 281 } 282 else { 283 pinfo = apr_pstrdup (p, ptr); 284 } 285 } 286 287 /* If we have a statCache then try to pull the information 288 from the cache. Otherwise just stat the file and return.*/ 289 if (statCache) { 290 apr_thread_rwlock_rdlock(rwlock); 291 pathctx = (NXPathCtx_t) apr_hash_get(statCache, ppath, APR_HASH_KEY_STRING); 292 apr_thread_rwlock_unlock(rwlock); 293 if (pathctx) { 294 return getstat(pathctx, pinfo, buf, requestmap); 295 } 296 else { 297 int err; 298 299 err = NXCreatePathContext(0, ppath, 0, NULL, &pathctx); 300 if (!err) { 301 apr_thread_rwlock_wrlock(rwlock); 302 apr_hash_set(statCache, apr_pstrdup(gPool,ppath) , APR_HASH_KEY_STRING, (void*)pathctx); 303 apr_thread_rwlock_unlock(rwlock); 304 return getstat(pathctx, pinfo, buf, requestmap); 305 } 306 } 307 } 308 } 309 return getstat(ctx, path, buf, requestmap); 310} 311#endif 312 313APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, 314 const char *fname, 315 apr_int32_t wanted, apr_pool_t *pool) 316{ 317 struct stat info; 318 int srv; 319 NXPathCtx_t pathCtx = 0; 320 321 getcwdpath(NULL, &pathCtx, CTX_ACTUAL_CWD); 322#ifdef APR_HAS_PSA 323 srv = getstat(pathCtx, (char*)fname, &info, ST_STAT_BITS|ST_NAME_BIT); 324#else 325 srv = cstat(pathCtx, (char*)fname, &info, ST_STAT_BITS|ST_NAME_BIT, pool); 326#endif 327 errno = srv; 328 329 if (srv == 0) { 330 finfo->pool = pool; 331 finfo->fname = fname; 332 fill_out_finfo(finfo, &info, wanted); 333 if (wanted & APR_FINFO_LINK) 334 wanted &= ~APR_FINFO_LINK; 335 if (wanted & APR_FINFO_NAME) { 336 finfo->name = apr_pstrdup(pool, info.st_name); 337 finfo->valid |= APR_FINFO_NAME; 338 } 339 return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS; 340 } 341 else { 342#if !defined(ENOENT) || !defined(ENOTDIR) 343#error ENOENT || ENOTDIR not defined; please see the 344#error comments at this line in the source for a workaround. 345 /* 346 * If ENOENT || ENOTDIR is not defined in one of the your OS's 347 * include files, APR cannot report a good reason why the stat() 348 * of the file failed; there are cases where it can fail even though 349 * the file exists. This opens holes in Apache, for example, because 350 * it becomes possible for someone to get a directory listing of a 351 * directory even though there is an index (eg. index.html) file in 352 * it. If you do not have a problem with this, delete the above 353 * #error lines and start the compile again. If you need to do this, 354 * please submit a bug report to http://www.apache.org/bug_report.html 355 * letting us know that you needed to do this. Please be sure to 356 * include the operating system you are using. 357 */ 358 /* WARNING: All errors will be handled as not found 359 */ 360#if !defined(ENOENT) 361 return APR_ENOENT; 362#else 363 /* WARNING: All errors but not found will be handled as not directory 364 */ 365 if (errno != ENOENT) 366 return APR_ENOENT; 367 else 368 return errno; 369#endif 370#else /* All was defined well, report the usual: */ 371 return errno; 372#endif 373 } 374} 375 376APR_DECLARE(apr_status_t) apr_file_mtime_set(const char *fname, 377 apr_time_t mtime, 378 apr_pool_t *pool) 379{ 380 apr_status_t status; 381 apr_finfo_t finfo; 382 383 status = apr_stat(&finfo, fname, APR_FINFO_ATIME, pool); 384 if (status) { 385 return status; 386 } 387 388#ifdef HAVE_UTIMES 389 { 390 struct timeval tvp[2]; 391 392 tvp[0].tv_sec = apr_time_sec(finfo.atime); 393 tvp[0].tv_usec = apr_time_usec(finfo.atime); 394 tvp[1].tv_sec = apr_time_sec(mtime); 395 tvp[1].tv_usec = apr_time_usec(mtime); 396 397 if (utimes(fname, tvp) == -1) { 398 return errno; 399 } 400 } 401#elif defined(HAVE_UTIME) 402 { 403 struct utimbuf buf; 404 405 buf.actime = (time_t) (finfo.atime / APR_USEC_PER_SEC); 406 buf.modtime = (time_t) (mtime / APR_USEC_PER_SEC); 407 408 if (utime(fname, &buf) == -1) { 409 return errno; 410 } 411 } 412#else 413 return APR_ENOTIMPL; 414#endif 415 416 return APR_SUCCESS; 417} 418