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