filestat.c revision 269847
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_file_io.h" 19#include "apr_general.h" 20#include "apr_strings.h" 21#include "apr_errno.h" 22 23#ifdef HAVE_UTIME 24#include <utime.h> 25#endif 26 27static apr_filetype_e filetype_from_mode(mode_t mode) 28{ 29 apr_filetype_e type; 30 31 switch (mode & S_IFMT) { 32 case S_IFREG: 33 type = APR_REG; break; 34 case S_IFDIR: 35 type = APR_DIR; break; 36 case S_IFLNK: 37 type = APR_LNK; break; 38 case S_IFCHR: 39 type = APR_CHR; break; 40 case S_IFBLK: 41 type = APR_BLK; break; 42#if defined(S_IFFIFO) 43 case S_IFFIFO: 44 type = APR_PIPE; break; 45#endif 46#if !defined(BEOS) && defined(S_IFSOCK) 47 case S_IFSOCK: 48 type = APR_SOCK; break; 49#endif 50 51 default: 52 /* Work around missing S_IFxxx values above 53 * for Linux et al. 54 */ 55#if !defined(S_IFFIFO) && defined(S_ISFIFO) 56 if (S_ISFIFO(mode)) { 57 type = APR_PIPE; 58 } else 59#endif 60#if !defined(BEOS) && !defined(S_IFSOCK) && defined(S_ISSOCK) 61 if (S_ISSOCK(mode)) { 62 type = APR_SOCK; 63 } else 64#endif 65 type = APR_UNKFILE; 66 } 67 return type; 68} 69 70static void fill_out_finfo(apr_finfo_t *finfo, struct_stat *info, 71 apr_int32_t wanted) 72{ 73 finfo->valid = APR_FINFO_MIN | APR_FINFO_IDENT | APR_FINFO_NLINK 74 | APR_FINFO_OWNER | APR_FINFO_PROT; 75 finfo->protection = apr_unix_mode2perms(info->st_mode); 76 finfo->filetype = filetype_from_mode(info->st_mode); 77 finfo->user = info->st_uid; 78 finfo->group = info->st_gid; 79 finfo->size = info->st_size; 80 finfo->device = info->st_dev; 81 finfo->nlink = info->st_nlink; 82 83 /* Check for overflow if storing a 64-bit st_ino in a 32-bit 84 * apr_ino_t for LFS builds: */ 85 if (sizeof(apr_ino_t) >= sizeof(info->st_ino) 86 || (apr_ino_t)info->st_ino == info->st_ino) { 87 finfo->inode = info->st_ino; 88 } else { 89 finfo->valid &= ~APR_FINFO_INODE; 90 } 91 92 apr_time_ansi_put(&finfo->atime, info->st_atime); 93#ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC 94 finfo->atime += info->st_atim.tv_nsec / APR_TIME_C(1000); 95#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC) 96 finfo->atime += info->st_atimensec / APR_TIME_C(1000); 97#elif defined(HAVE_STRUCT_STAT_ST_ATIME_N) 98 finfo->atime += info->st_atime_n / APR_TIME_C(1000); 99#endif 100 101 apr_time_ansi_put(&finfo->mtime, info->st_mtime); 102#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 103 finfo->mtime += info->st_mtim.tv_nsec / APR_TIME_C(1000); 104#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC) 105 finfo->mtime += info->st_mtimensec / APR_TIME_C(1000); 106#elif defined(HAVE_STRUCT_STAT_ST_MTIME_N) 107 finfo->mtime += info->st_mtime_n / APR_TIME_C(1000); 108#endif 109 110 apr_time_ansi_put(&finfo->ctime, info->st_ctime); 111#ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC 112 finfo->ctime += info->st_ctim.tv_nsec / APR_TIME_C(1000); 113#elif defined(HAVE_STRUCT_STAT_ST_CTIMENSEC) 114 finfo->ctime += info->st_ctimensec / APR_TIME_C(1000); 115#elif defined(HAVE_STRUCT_STAT_ST_CTIME_N) 116 finfo->ctime += info->st_ctime_n / APR_TIME_C(1000); 117#endif 118 119#ifdef HAVE_STRUCT_STAT_ST_BLOCKS 120#ifdef DEV_BSIZE 121 finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)DEV_BSIZE; 122#else 123 finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)512; 124#endif 125 finfo->valid |= APR_FINFO_CSIZE; 126#endif 127} 128 129apr_status_t apr_file_info_get_locked(apr_finfo_t *finfo, apr_int32_t wanted, 130 apr_file_t *thefile) 131{ 132 struct_stat info; 133 134 if (thefile->buffered) { 135 apr_status_t rv = apr_file_flush_locked(thefile); 136 if (rv != APR_SUCCESS) 137 return rv; 138 } 139 140 if (fstat(thefile->filedes, &info) == 0) { 141 finfo->pool = thefile->pool; 142 finfo->fname = thefile->fname; 143 fill_out_finfo(finfo, &info, wanted); 144 return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS; 145 } 146 else { 147 return errno; 148 } 149} 150 151APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, 152 apr_int32_t wanted, 153 apr_file_t *thefile) 154{ 155 struct_stat info; 156 157 if (thefile->buffered) { 158 apr_status_t rv = apr_file_flush(thefile); 159 if (rv != APR_SUCCESS) 160 return rv; 161 } 162 163 if (fstat(thefile->filedes, &info) == 0) { 164 finfo->pool = thefile->pool; 165 finfo->fname = thefile->fname; 166 fill_out_finfo(finfo, &info, wanted); 167 return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS; 168 } 169 else { 170 return errno; 171 } 172} 173 174APR_DECLARE(apr_status_t) apr_file_perms_set(const char *fname, 175 apr_fileperms_t perms) 176{ 177 mode_t mode = apr_unix_perms2mode(perms); 178 179 if (chmod(fname, mode) == -1) 180 return errno; 181 return APR_SUCCESS; 182} 183 184APR_DECLARE(apr_status_t) apr_file_attrs_set(const char *fname, 185 apr_fileattrs_t attributes, 186 apr_fileattrs_t attr_mask, 187 apr_pool_t *pool) 188{ 189 apr_status_t status; 190 apr_finfo_t finfo; 191 192 /* Don't do anything if we can't handle the requested attributes */ 193 if (!(attr_mask & (APR_FILE_ATTR_READONLY 194 | APR_FILE_ATTR_EXECUTABLE))) 195 return APR_SUCCESS; 196 197 status = apr_stat(&finfo, fname, APR_FINFO_PROT, pool); 198 if (status) 199 return status; 200 201 /* ### TODO: should added bits be umask'd? */ 202 if (attr_mask & APR_FILE_ATTR_READONLY) 203 { 204 if (attributes & APR_FILE_ATTR_READONLY) 205 { 206 finfo.protection &= ~APR_UWRITE; 207 finfo.protection &= ~APR_GWRITE; 208 finfo.protection &= ~APR_WWRITE; 209 } 210 else 211 { 212 /* ### umask this! */ 213 finfo.protection |= APR_UWRITE; 214 finfo.protection |= APR_GWRITE; 215 finfo.protection |= APR_WWRITE; 216 } 217 } 218 219 if (attr_mask & APR_FILE_ATTR_EXECUTABLE) 220 { 221 if (attributes & APR_FILE_ATTR_EXECUTABLE) 222 { 223 /* ### umask this! */ 224 finfo.protection |= APR_UEXECUTE; 225 finfo.protection |= APR_GEXECUTE; 226 finfo.protection |= APR_WEXECUTE; 227 } 228 else 229 { 230 finfo.protection &= ~APR_UEXECUTE; 231 finfo.protection &= ~APR_GEXECUTE; 232 finfo.protection &= ~APR_WEXECUTE; 233 } 234 } 235 236 return apr_file_perms_set(fname, finfo.protection); 237} 238 239 240APR_DECLARE(apr_status_t) apr_file_mtime_set(const char *fname, 241 apr_time_t mtime, 242 apr_pool_t *pool) 243{ 244 apr_status_t status; 245 apr_finfo_t finfo; 246 247 status = apr_stat(&finfo, fname, APR_FINFO_ATIME, pool); 248 if (status) { 249 return status; 250 } 251 252#ifdef HAVE_UTIMES 253 { 254 struct timeval tvp[2]; 255 256 tvp[0].tv_sec = apr_time_sec(finfo.atime); 257 tvp[0].tv_usec = apr_time_usec(finfo.atime); 258 tvp[1].tv_sec = apr_time_sec(mtime); 259 tvp[1].tv_usec = apr_time_usec(mtime); 260 261 if (utimes(fname, tvp) == -1) { 262 return errno; 263 } 264 } 265#elif defined(HAVE_UTIME) 266 { 267 struct utimbuf buf; 268 269 buf.actime = (time_t) (finfo.atime / APR_USEC_PER_SEC); 270 buf.modtime = (time_t) (mtime / APR_USEC_PER_SEC); 271 272 if (utime(fname, &buf) == -1) { 273 return errno; 274 } 275 } 276#else 277 return APR_ENOTIMPL; 278#endif 279 280 return APR_SUCCESS; 281} 282 283 284APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, 285 const char *fname, 286 apr_int32_t wanted, apr_pool_t *pool) 287{ 288 struct_stat info; 289 int srv; 290 291 if (wanted & APR_FINFO_LINK) 292 srv = lstat(fname, &info); 293 else 294 srv = stat(fname, &info); 295 296 if (srv == 0) { 297 finfo->pool = pool; 298 finfo->fname = fname; 299 fill_out_finfo(finfo, &info, wanted); 300 if (wanted & APR_FINFO_LINK) 301 wanted &= ~APR_FINFO_LINK; 302 return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS; 303 } 304 else { 305#if !defined(ENOENT) || !defined(ENOTDIR) 306#error ENOENT || ENOTDIR not defined; please see the 307#error comments at this line in the source for a workaround. 308 /* 309 * If ENOENT || ENOTDIR is not defined in one of the your OS's 310 * include files, APR cannot report a good reason why the stat() 311 * of the file failed; there are cases where it can fail even though 312 * the file exists. This opens holes in Apache, for example, because 313 * it becomes possible for someone to get a directory listing of a 314 * directory even though there is an index (eg. index.html) file in 315 * it. If you do not have a problem with this, delete the above 316 * #error lines and start the compile again. If you need to do this, 317 * please submit a bug report to http://www.apache.org/bug_report.html 318 * letting us know that you needed to do this. Please be sure to 319 * include the operating system you are using. 320 */ 321 /* WARNING: All errors will be handled as not found 322 */ 323#if !defined(ENOENT) 324 return APR_ENOENT; 325#else 326 /* WARNING: All errors but not found will be handled as not directory 327 */ 328 if (errno != ENOENT) 329 return APR_ENOENT; 330 else 331 return errno; 332#endif 333#else /* All was defined well, report the usual: */ 334 return errno; 335#endif 336 } 337} 338 339 340