filestat.c revision 251886
11541Srgrimes/* Licensed to the Apache Software Foundation (ASF) under one or more
21541Srgrimes * contributor license agreements.  See the NOTICE file distributed with
31541Srgrimes * this work for additional information regarding copyright ownership.
41541Srgrimes * The ASF licenses this file to You under the Apache License, Version 2.0
51541Srgrimes * (the "License"); you may not use this file except in compliance with
61541Srgrimes * the License.  You may obtain a copy of the License at
71541Srgrimes *
81541Srgrimes *     http://www.apache.org/licenses/LICENSE-2.0
91541Srgrimes *
101541Srgrimes * Unless required by applicable law or agreed to in writing, software
111541Srgrimes * distributed under the License is distributed on an "AS IS" BASIS,
121541Srgrimes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131541Srgrimes * See the License for the specific language governing permissions and
141541Srgrimes * limitations under the License.
151541Srgrimes */
161541Srgrimes
171541Srgrimes#include "apr_arch_file_io.h"
181541Srgrimes#include "apr_file_io.h"
191541Srgrimes#include "apr_general.h"
201541Srgrimes#include "apr_strings.h"
211541Srgrimes#include "apr_errno.h"
221541Srgrimes
231541Srgrimes#ifdef HAVE_UTIME
241541Srgrimes#include <utime.h>
251541Srgrimes#endif
261541Srgrimes
271541Srgrimesstatic apr_filetype_e filetype_from_mode(mode_t mode)
281541Srgrimes{
291541Srgrimes    apr_filetype_e type;
301541Srgrimes
311541Srgrimes    switch (mode & S_IFMT) {
321541Srgrimes    case S_IFREG:
331541Srgrimes        type = APR_REG;  break;
3450477Speter    case S_IFDIR:
351541Srgrimes        type = APR_DIR;  break;
361541Srgrimes    case S_IFLNK:
372169Spaul        type = APR_LNK;  break;
382169Spaul    case S_IFCHR:
392169Spaul        type = APR_CHR;  break;
40104343Smike    case S_IFBLK:
41104343Smike        type = APR_BLK;  break;
42104342Smike#if defined(S_IFFIFO)
43104342Smike    case S_IFFIFO:
4437622Sbde        type = APR_PIPE; break;
4537622Sbde#endif
466247Swollman#if !defined(BEOS) && defined(S_IFSOCK)
4755679Sshin    case S_IFSOCK:
4855679Sshin        type = APR_SOCK; break;
4955679Sshin#endif
501541Srgrimes
511541Srgrimes    default:
521541Srgrimes	/* Work around missing S_IFxxx values above
531541Srgrimes         * for Linux et al.
541541Srgrimes         */
551541Srgrimes#if !defined(S_IFFIFO) && defined(S_ISFIFO)
561541Srgrimes    	if (S_ISFIFO(mode)) {
571541Srgrimes            type = APR_PIPE;
581541Srgrimes	} else
598876Srgrimes#endif
6036767Sbde#if !defined(BEOS) && !defined(S_IFSOCK) && defined(S_ISSOCK)
611541Srgrimes    	if (S_ISSOCK(mode)) {
621541Srgrimes            type = APR_SOCK;
638876Srgrimes	} else
6436767Sbde#endif
651541Srgrimes        type = APR_UNKFILE;
661541Srgrimes    }
671541Srgrimes    return type;
681541Srgrimes}
691541Srgrimes
701541Srgrimesstatic void fill_out_finfo(apr_finfo_t *finfo, struct_stat *info,
711541Srgrimes                           apr_int32_t wanted)
721541Srgrimes{
731541Srgrimes    finfo->valid = APR_FINFO_MIN | APR_FINFO_IDENT | APR_FINFO_NLINK
7470826Srwatson                 | APR_FINFO_OWNER | APR_FINFO_PROT;
7570826Srwatson    finfo->protection = apr_unix_mode2perms(info->st_mode);
7670854Srwatson    finfo->filetype = filetype_from_mode(info->st_mode);
776348Swollman    finfo->user = info->st_uid;
781541Srgrimes    finfo->group = info->st_gid;
791541Srgrimes    finfo->size = info->st_size;
801541Srgrimes    finfo->device = info->st_dev;
811541Srgrimes    finfo->nlink = info->st_nlink;
821541Srgrimes
831541Srgrimes    /* Check for overflow if storing a 64-bit st_ino in a 32-bit
841541Srgrimes     * apr_ino_t for LFS builds: */
851541Srgrimes    if (sizeof(apr_ino_t) >= sizeof(info->st_ino)
861541Srgrimes        || (apr_ino_t)info->st_ino == info->st_ino) {
871541Srgrimes        finfo->inode = info->st_ino;
881541Srgrimes    } else {
891541Srgrimes        finfo->valid &= ~APR_FINFO_INODE;
901541Srgrimes    }
911541Srgrimes
921541Srgrimes    apr_time_ansi_put(&finfo->atime, info->st_atime);
931541Srgrimes#ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
941541Srgrimes    finfo->atime += info->st_atim.tv_nsec / APR_TIME_C(1000);
956247Swollman#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
961541Srgrimes    finfo->atime += info->st_atimensec / APR_TIME_C(1000);
971541Srgrimes#elif defined(HAVE_STRUCT_STAT_ST_ATIME_N)
986247Swollman    finfo->ctime += info->st_atime_n / APR_TIME_C(1000);
996247Swollman#endif
1006247Swollman
1016247Swollman    apr_time_ansi_put(&finfo->mtime, info->st_mtime);
1026247Swollman#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
1036247Swollman    finfo->mtime += info->st_mtim.tv_nsec / APR_TIME_C(1000);
1046247Swollman#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
1056247Swollman    finfo->mtime += info->st_mtimensec / APR_TIME_C(1000);
1061541Srgrimes#elif defined(HAVE_STRUCT_STAT_ST_MTIME_N)
1071541Srgrimes    finfo->ctime += info->st_mtime_n / APR_TIME_C(1000);
108124258Sandre#endif
1091541Srgrimes
1101541Srgrimes    apr_time_ansi_put(&finfo->ctime, info->st_ctime);
1111541Srgrimes#ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC
1121541Srgrimes    finfo->ctime += info->st_ctim.tv_nsec / APR_TIME_C(1000);
113124258Sandre#elif defined(HAVE_STRUCT_STAT_ST_CTIMENSEC)
114124290Sandre    finfo->ctime += info->st_ctimensec / APR_TIME_C(1000);
115124290Sandre#elif defined(HAVE_STRUCT_STAT_ST_CTIME_N)
116124258Sandre    finfo->ctime += info->st_ctime_n / APR_TIME_C(1000);
117124258Sandre#endif
118124258Sandre
119124258Sandre#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
120124258Sandre#ifdef DEV_BSIZE
121124290Sandre    finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)DEV_BSIZE;
122124258Sandre#else
123124258Sandre    finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)512;
124124258Sandre#endif
125124258Sandre    finfo->valid |= APR_FINFO_CSIZE;
126124258Sandre#endif
127124258Sandre}
128124258Sandre
129124437Sandreapr_status_t apr_file_info_get_locked(apr_finfo_t *finfo, apr_int32_t wanted,
1301541Srgrimes                                      apr_file_t *thefile)
13152904Sshin{
13252904Sshin    struct_stat info;
13352904Sshin
13452904Sshin    if (thefile->buffered) {
13552904Sshin        apr_status_t rv = apr_file_flush_locked(thefile);
13652904Sshin        if (rv != APR_SUCCESS)
13752904Sshin            return rv;
13852904Sshin    }
1391541Srgrimes
1406247Swollman    if (fstat(thefile->filedes, &info) == 0) {
1411541Srgrimes        finfo->pool = thefile->pool;
1421541Srgrimes        finfo->fname = thefile->fname;
1431541Srgrimes        fill_out_finfo(finfo, &info, wanted);
14460067Sjlemon        return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
14560067Sjlemon    }
1466247Swollman    else {
1476247Swollman        return errno;
1486247Swollman    }
149104342Smike}
1506247Swollman
1511541SrgrimesAPR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo,
1521541Srgrimes                                            apr_int32_t wanted,
1531541Srgrimes                                            apr_file_t *thefile)
1541541Srgrimes{
155104342Smike    struct_stat info;
1561541Srgrimes
1576247Swollman    if (thefile->buffered) {
1586247Swollman        apr_status_t rv = apr_file_flush(thefile);
159104342Smike        if (rv != APR_SUCCESS)
1602169Spaul            return rv;
161104342Smike    }
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