filestat.c revision 251875
1185377Ssam/* Licensed to the Apache Software Foundation (ASF) under one or more 2185377Ssam * contributor license agreements. See the NOTICE file distributed with 3185377Ssam * this work for additional information regarding copyright ownership. 4185377Ssam * The ASF licenses this file to You under the Apache License, Version 2.0 5185377Ssam * (the "License"); you may not use this file except in compliance with 6185377Ssam * the License. You may obtain a copy of the License at 7185377Ssam * 8185377Ssam * http://www.apache.org/licenses/LICENSE-2.0 9185377Ssam * 10185377Ssam * Unless required by applicable law or agreed to in writing, software 11185377Ssam * distributed under the License is distributed on an "AS IS" BASIS, 12185377Ssam * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13185377Ssam * See the License for the specific language governing permissions and 14185377Ssam * limitations under the License. 15185377Ssam */ 16185377Ssam 17186015Ssam#include "apr_arch_file_io.h" 18185377Ssam#include "apr_file_io.h" 19185377Ssam#include "apr_general.h" 20185377Ssam#include "apr_strings.h" 21185377Ssam#include "apr_errno.h" 22185377Ssam 23185377Ssam#ifdef HAVE_UTIME 24185377Ssam#include <utime.h> 25185377Ssam#endif 26185377Ssam 27185377Ssamstatic apr_filetype_e filetype_from_mode(mode_t mode) 28185377Ssam{ 29185377Ssam apr_filetype_e type; 30185377Ssam 31185377Ssam switch (mode & S_IFMT) { 32185377Ssam case S_IFREG: 33185377Ssam type = APR_REG; break; 34185377Ssam case S_IFDIR: 35185377Ssam type = APR_DIR; break; 36185377Ssam case S_IFLNK: 37185377Ssam type = APR_LNK; break; 38185377Ssam case S_IFCHR: 39185377Ssam type = APR_CHR; break; 40185377Ssam case S_IFBLK: 41185377Ssam type = APR_BLK; break; 42185377Ssam#if defined(S_IFFIFO) 43185377Ssam case S_IFFIFO: 44185377Ssam type = APR_PIPE; break; 45185377Ssam#endif 46185377Ssam#if !defined(BEOS) && defined(S_IFSOCK) 47185377Ssam case S_IFSOCK: 48185377Ssam type = APR_SOCK; break; 49185377Ssam#endif 50185377Ssam 51185377Ssam default: 52185377Ssam /* Work around missing S_IFxxx values above 53185377Ssam * for Linux et al. 54185377Ssam */ 55185377Ssam#if !defined(S_IFFIFO) && defined(S_ISFIFO) 56185377Ssam if (S_ISFIFO(mode)) { 57185377Ssam type = APR_PIPE; 58185377Ssam } else 59185377Ssam#endif 60185406Ssam#if !defined(BEOS) && !defined(S_IFSOCK) && defined(S_ISSOCK) 61185377Ssam if (S_ISSOCK(mode)) { 62185406Ssam type = APR_SOCK; 63185377Ssam } else 64217624Sadrian#endif 65272292Sadrian type = APR_UNKFILE; 66185377Ssam } 67185377Ssam return type; 68185377Ssam} 69185406Ssam 70185377Ssamstatic void fill_out_finfo(apr_finfo_t *finfo, struct_stat *info, 71185377Ssam apr_int32_t wanted) 72185377Ssam{ 73185377Ssam finfo->valid = APR_FINFO_MIN | APR_FINFO_IDENT | APR_FINFO_NLINK 74225883Sadrian | APR_FINFO_OWNER | APR_FINFO_PROT; 75185377Ssam finfo->protection = apr_unix_mode2perms(info->st_mode); 76185377Ssam finfo->filetype = filetype_from_mode(info->st_mode); 77185377Ssam finfo->user = info->st_uid; 78185377Ssam finfo->group = info->st_gid; 79185377Ssam finfo->size = info->st_size; 80225883Sadrian finfo->device = info->st_dev; 81185377Ssam finfo->nlink = info->st_nlink; 82185377Ssam 83185377Ssam /* Check for overflow if storing a 64-bit st_ino in a 32-bit 84185377Ssam * apr_ino_t for LFS builds: */ 85185377Ssam if (sizeof(apr_ino_t) >= sizeof(info->st_ino) 86185377Ssam || (apr_ino_t)info->st_ino == info->st_ino) { 87185377Ssam finfo->inode = info->st_ino; 88185377Ssam } else { 89185377Ssam finfo->valid &= ~APR_FINFO_INODE; 90185377Ssam } 91185377Ssam 92185377Ssam apr_time_ansi_put(&finfo->atime, info->st_atime); 93185377Ssam#ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC 94185377Ssam finfo->atime += info->st_atim.tv_nsec / APR_TIME_C(1000); 95185377Ssam#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC) 96185377Ssam finfo->atime += info->st_atimensec / APR_TIME_C(1000); 97185377Ssam#elif defined(HAVE_STRUCT_STAT_ST_ATIME_N) 98185377Ssam finfo->ctime += info->st_atime_n / APR_TIME_C(1000); 99185377Ssam#endif 100185377Ssam 101185377Ssam apr_time_ansi_put(&finfo->mtime, info->st_mtime); 102185377Ssam#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 103185377Ssam finfo->mtime += info->st_mtim.tv_nsec / APR_TIME_C(1000); 104185377Ssam#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC) 105185377Ssam finfo->mtime += info->st_mtimensec / APR_TIME_C(1000); 106185377Ssam#elif defined(HAVE_STRUCT_STAT_ST_MTIME_N) 107185377Ssam finfo->ctime += info->st_mtime_n / APR_TIME_C(1000); 108185377Ssam#endif 109185377Ssam 110185377Ssam apr_time_ansi_put(&finfo->ctime, info->st_ctime); 111185377Ssam#ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC 112185377Ssam finfo->ctime += info->st_ctim.tv_nsec / APR_TIME_C(1000); 113185377Ssam#elif defined(HAVE_STRUCT_STAT_ST_CTIMENSEC) 114185377Ssam finfo->ctime += info->st_ctimensec / APR_TIME_C(1000); 115185377Ssam#elif defined(HAVE_STRUCT_STAT_ST_CTIME_N) 116185377Ssam finfo->ctime += info->st_ctime_n / APR_TIME_C(1000); 117185377Ssam#endif 118185377Ssam 119185377Ssam#ifdef HAVE_STRUCT_STAT_ST_BLOCKS 120185377Ssam#ifdef DEV_BSIZE 121185377Ssam finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)DEV_BSIZE; 122185377Ssam#else 123185377Ssam finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)512; 124185377Ssam#endif 125185377Ssam finfo->valid |= APR_FINFO_CSIZE; 126186098Ssam#endif 127185377Ssam} 128185377Ssam 129185377Ssamapr_status_t apr_file_info_get_locked(apr_finfo_t *finfo, apr_int32_t wanted, 130185377Ssam apr_file_t *thefile) 131185377Ssam{ 132185377Ssam struct_stat info; 133185377Ssam 134185377Ssam if (thefile->buffered) { 135185377Ssam apr_status_t rv = apr_file_flush_locked(thefile); 136185377Ssam if (rv != APR_SUCCESS) 137185377Ssam return rv; 138185377Ssam } 139185377Ssam 140185377Ssam if (fstat(thefile->filedes, &info) == 0) { 141185377Ssam finfo->pool = thefile->pool; 142185377Ssam finfo->fname = thefile->fname; 143185377Ssam fill_out_finfo(finfo, &info, wanted); 144185377Ssam return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS; 145185377Ssam } 146185377Ssam else { 147185377Ssam return errno; 148185377Ssam } 149185377Ssam} 150185377Ssam 151185377SsamAPR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, 152185377Ssam apr_int32_t wanted, 153185377Ssam apr_file_t *thefile) 154185377Ssam{ 155185377Ssam struct_stat info; 156185377Ssam 157185377Ssam if (thefile->buffered) { 158185377Ssam apr_status_t rv = apr_file_flush(thefile); 159185377Ssam if (rv != APR_SUCCESS) 160185377Ssam return rv; 161185377Ssam } 162185377Ssam 163185377Ssam if (fstat(thefile->filedes, &info) == 0) { 164185377Ssam finfo->pool = thefile->pool; 165185377Ssam finfo->fname = thefile->fname; 166185377Ssam fill_out_finfo(finfo, &info, wanted); 167185377Ssam return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS; 168185377Ssam } 169185377Ssam else { 170185377Ssam return errno; 171185377Ssam } 172185377Ssam} 173185377Ssam 174185377SsamAPR_DECLARE(apr_status_t) apr_file_perms_set(const char *fname, 175185377Ssam apr_fileperms_t perms) 176185377Ssam{ 177185377Ssam mode_t mode = apr_unix_perms2mode(perms); 178185377Ssam 179185377Ssam if (chmod(fname, mode) == -1) 180185377Ssam return errno; 181185377Ssam return APR_SUCCESS; 182185377Ssam} 183185377Ssam 184185377SsamAPR_DECLARE(apr_status_t) apr_file_attrs_set(const char *fname, 185185377Ssam apr_fileattrs_t attributes, 186185377Ssam apr_fileattrs_t attr_mask, 187185377Ssam apr_pool_t *pool) 188185406Ssam{ 189185406Ssam apr_status_t status; 190185406Ssam apr_finfo_t finfo; 191185377Ssam 192185380Ssam /* Don't do anything if we can't handle the requested attributes */ 193185377Ssam if (!(attr_mask & (APR_FILE_ATTR_READONLY 194185377Ssam | APR_FILE_ATTR_EXECUTABLE))) 195185377Ssam return APR_SUCCESS; 196185377Ssam 197185377Ssam status = apr_stat(&finfo, fname, APR_FINFO_PROT, pool); 198185377Ssam if (status) 199185377Ssam return status; 200185377Ssam 201185377Ssam /* ### TODO: should added bits be umask'd? */ 202185377Ssam if (attr_mask & APR_FILE_ATTR_READONLY) 203185377Ssam { 204185377Ssam if (attributes & APR_FILE_ATTR_READONLY) 205185377Ssam { 206185377Ssam finfo.protection &= ~APR_UWRITE; 207185377Ssam finfo.protection &= ~APR_GWRITE; 208185377Ssam finfo.protection &= ~APR_WWRITE; 209185377Ssam } 210185377Ssam else 211185377Ssam { 212185377Ssam /* ### umask this! */ 213185377Ssam finfo.protection |= APR_UWRITE; 214185377Ssam finfo.protection |= APR_GWRITE; 215185377Ssam finfo.protection |= APR_WWRITE; 216185377Ssam } 217185377Ssam } 218185377Ssam 219185377Ssam if (attr_mask & APR_FILE_ATTR_EXECUTABLE) 220185377Ssam { 221185377Ssam if (attributes & APR_FILE_ATTR_EXECUTABLE) 222185377Ssam { 223185377Ssam /* ### umask this! */ 224185377Ssam finfo.protection |= APR_UEXECUTE; 225185377Ssam finfo.protection |= APR_GEXECUTE; 226185377Ssam finfo.protection |= APR_WEXECUTE; 227185377Ssam } 228185377Ssam else 229185377Ssam { 230185377Ssam finfo.protection &= ~APR_UEXECUTE; 231185377Ssam finfo.protection &= ~APR_GEXECUTE; 232185377Ssam finfo.protection &= ~APR_WEXECUTE; 233185377Ssam } 234185377Ssam } 235185377Ssam 236185377Ssam return apr_file_perms_set(fname, finfo.protection); 237185377Ssam} 238185377Ssam 239185377Ssam 240185377SsamAPR_DECLARE(apr_status_t) apr_file_mtime_set(const char *fname, 241185377Ssam apr_time_t mtime, 242185377Ssam apr_pool_t *pool) 243185377Ssam{ 244185377Ssam apr_status_t status; 245185377Ssam apr_finfo_t finfo; 246185377Ssam 247185377Ssam status = apr_stat(&finfo, fname, APR_FINFO_ATIME, pool); 248185377Ssam if (status) { 249185377Ssam return status; 250185377Ssam } 251185377Ssam 252185377Ssam#ifdef HAVE_UTIMES 253185377Ssam { 254185377Ssam struct timeval tvp[2]; 255185406Ssam 256185377Ssam tvp[0].tv_sec = apr_time_sec(finfo.atime); 257185377Ssam tvp[0].tv_usec = apr_time_usec(finfo.atime); 258185377Ssam tvp[1].tv_sec = apr_time_sec(mtime); 259185377Ssam tvp[1].tv_usec = apr_time_usec(mtime); 260185377Ssam 261185377Ssam if (utimes(fname, tvp) == -1) { 262185377Ssam return errno; 263185377Ssam } 264185377Ssam } 265185377Ssam#elif defined(HAVE_UTIME) 266185377Ssam { 267185377Ssam struct utimbuf buf; 268185377Ssam 269185377Ssam buf.actime = (time_t) (finfo.atime / APR_USEC_PER_SEC); 270185377Ssam buf.modtime = (time_t) (mtime / APR_USEC_PER_SEC); 271185377Ssam 272185377Ssam if (utime(fname, &buf) == -1) { 273185377Ssam return errno; 274185377Ssam } 275185377Ssam } 276185377Ssam#else 277185377Ssam return APR_ENOTIMPL; 278185377Ssam#endif 279185377Ssam 280185377Ssam return APR_SUCCESS; 281185377Ssam} 282185377Ssam 283185377Ssam 284185377SsamAPR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, 285185377Ssam const char *fname, 286185377Ssam apr_int32_t wanted, apr_pool_t *pool) 287185377Ssam{ 288185377Ssam struct_stat info; 289185377Ssam int srv; 290185377Ssam 291185377Ssam if (wanted & APR_FINFO_LINK) 292185377Ssam srv = lstat(fname, &info); 293185377Ssam else 294185377Ssam srv = stat(fname, &info); 295185377Ssam 296185377Ssam if (srv == 0) { 297185377Ssam finfo->pool = pool; 298185377Ssam finfo->fname = fname; 299185377Ssam fill_out_finfo(finfo, &info, wanted); 300185377Ssam if (wanted & APR_FINFO_LINK) 301185377Ssam wanted &= ~APR_FINFO_LINK; 302185377Ssam return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS; 303185377Ssam } 304185377Ssam else { 305185377Ssam#if !defined(ENOENT) || !defined(ENOTDIR) 306185377Ssam#error ENOENT || ENOTDIR not defined; please see the 307185377Ssam#error comments at this line in the source for a workaround. 308185377Ssam /* 309185377Ssam * If ENOENT || ENOTDIR is not defined in one of the your OS's 310185406Ssam * include files, APR cannot report a good reason why the stat() 311185406Ssam * of the file failed; there are cases where it can fail even though 312185406Ssam * the file exists. This opens holes in Apache, for example, because 313185406Ssam * it becomes possible for someone to get a directory listing of a 314185406Ssam * directory even though there is an index (eg. index.html) file in 315185406Ssam * it. If you do not have a problem with this, delete the above 316185406Ssam * #error lines and start the compile again. If you need to do this, 317185406Ssam * please submit a bug report to http://www.apache.org/bug_report.html 318185406Ssam * letting us know that you needed to do this. Please be sure to 319185406Ssam * include the operating system you are using. 320185406Ssam */ 321185406Ssam /* WARNING: All errors will be handled as not found 322185406Ssam */ 323185406Ssam#if !defined(ENOENT) 324185406Ssam return APR_ENOENT; 325188549Ssam#else 326185406Ssam /* WARNING: All errors but not found will be handled as not directory 327185406Ssam */ 328185406Ssam if (errno != ENOENT) 329185406Ssam return APR_ENOENT; 330185406Ssam else 331185406Ssam return errno; 332185406Ssam#endif 333185406Ssam#else /* All was defined well, report the usual: */ 334185406Ssam return errno; 335185418Ssam#endif 336 } 337} 338 339 340