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