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 "fsio.h"
19 #include "nks/dirio.h"
20 #include "apr_file_io.h"
21 #include "apr_general.h"
22 #include "apr_strings.h"
23 #include "apr_errno.h"
24 #include "apr_hash.h"
25 #include "apr_thread_rwlock.h"
26 
27 #ifdef HAVE_UTIME_H
28 #include <utime.h>
29 #endif
30 
31 #define APR_HAS_PSA
32 
filetype_from_mode(mode_t mode)33 static apr_filetype_e filetype_from_mode(mode_t mode)
34 {
35     apr_filetype_e type = APR_NOFILE;
36 
37     if (S_ISREG(mode))
38         type = APR_REG;
39     else if (S_ISDIR(mode))
40         type = APR_DIR;
41     else if (S_ISCHR(mode))
42         type = APR_CHR;
43     else if (S_ISBLK(mode))
44         type = APR_BLK;
45     else if (S_ISFIFO(mode))
46         type = APR_PIPE;
47     else if (S_ISLNK(mode))
48         type = APR_LNK;
49     else if (S_ISSOCK(mode))
50         type = APR_SOCK;
51     else
52         type = APR_UNKFILE;
53     return type;
54 }
55 
fill_out_finfo(apr_finfo_t * finfo,struct stat * info,apr_int32_t wanted)56 static void fill_out_finfo(apr_finfo_t *finfo, struct stat *info,
57                            apr_int32_t wanted)
58 {
59     finfo->valid = APR_FINFO_MIN | APR_FINFO_IDENT | APR_FINFO_NLINK
60                     | APR_FINFO_OWNER | APR_FINFO_PROT;
61     finfo->protection = apr_unix_mode2perms(info->st_mode);
62     finfo->filetype = filetype_from_mode(info->st_mode);
63     finfo->user = info->st_uid;
64     finfo->group = info->st_gid;
65     finfo->size = info->st_size;
66     finfo->inode = info->st_ino;
67     finfo->device = info->st_dev;
68     finfo->nlink = info->st_nlink;
69     apr_time_ansi_put(&finfo->atime, info->st_atime.tv_sec);
70     apr_time_ansi_put(&finfo->mtime, info->st_mtime.tv_sec);
71     apr_time_ansi_put(&finfo->ctime, info->st_ctime.tv_sec);
72     /* ### needs to be revisited
73      * if (wanted & APR_FINFO_CSIZE) {
74      *   finfo->csize = info->st_blocks * 512;
75      *   finfo->valid |= APR_FINFO_CSIZE;
76      * }
77      */
78 }
79 
apr_file_info_get(apr_finfo_t * finfo,apr_int32_t wanted,apr_file_t * thefile)80 APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo,
81                                             apr_int32_t wanted,
82                                             apr_file_t *thefile)
83 {
84     struct stat info;
85 
86     if (thefile->buffered) {
87         apr_status_t rv = apr_file_flush(thefile);
88         if (rv != APR_SUCCESS)
89             return rv;
90     }
91 
92     if (fstat(thefile->filedes, &info) == 0) {
93         finfo->pool = thefile->pool;
94         finfo->fname = thefile->fname;
95         fill_out_finfo(finfo, &info, wanted);
96         return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
97     }
98     else {
99         return errno;
100     }
101 }
102 
apr_file_perms_set(const char * fname,apr_fileperms_t perms)103 APR_DECLARE(apr_status_t) apr_file_perms_set(const char *fname,
104                                              apr_fileperms_t perms)
105 {
106     mode_t mode = apr_unix_perms2mode(perms);
107 
108     if (chmod(fname, mode) == -1)
109         return errno;
110     return APR_SUCCESS;
111 }
112 
apr_file_attrs_set(const char * fname,apr_fileattrs_t attributes,apr_fileattrs_t attr_mask,apr_pool_t * pool)113 APR_DECLARE(apr_status_t) apr_file_attrs_set(const char *fname,
114                                              apr_fileattrs_t attributes,
115                                              apr_fileattrs_t attr_mask,
116                                              apr_pool_t *pool)
117 {
118     apr_status_t status;
119     apr_finfo_t finfo;
120 
121     /* Don't do anything if we can't handle the requested attributes */
122     if (!(attr_mask & (APR_FILE_ATTR_READONLY
123                        | APR_FILE_ATTR_EXECUTABLE)))
124         return APR_SUCCESS;
125 
126     status = apr_stat(&finfo, fname, APR_FINFO_PROT, pool);
127     if (status)
128         return status;
129 
130     /* ### TODO: should added bits be umask'd? */
131     if (attr_mask & APR_FILE_ATTR_READONLY)
132     {
133         if (attributes & APR_FILE_ATTR_READONLY)
134         {
135             finfo.protection &= ~APR_UWRITE;
136             finfo.protection &= ~APR_GWRITE;
137             finfo.protection &= ~APR_WWRITE;
138         }
139         else
140         {
141             /* ### umask this! */
142             finfo.protection |= APR_UWRITE;
143             finfo.protection |= APR_GWRITE;
144             finfo.protection |= APR_WWRITE;
145         }
146     }
147 
148     if (attr_mask & APR_FILE_ATTR_EXECUTABLE)
149     {
150         if (attributes & APR_FILE_ATTR_EXECUTABLE)
151         {
152             /* ### umask this! */
153             finfo.protection |= APR_UEXECUTE;
154             finfo.protection |= APR_GEXECUTE;
155             finfo.protection |= APR_WEXECUTE;
156         }
157         else
158         {
159             finfo.protection &= ~APR_UEXECUTE;
160             finfo.protection &= ~APR_GEXECUTE;
161             finfo.protection &= ~APR_WEXECUTE;
162         }
163     }
164 
165     return apr_file_perms_set(fname, finfo.protection);
166 }
167 
168 #ifndef APR_HAS_PSA
stat_cache_cleanup(void * data)169 static apr_status_t stat_cache_cleanup(void *data)
170 {
171     apr_pool_t *p = (apr_pool_t *)getGlobalPool();
172     apr_hash_index_t *hi;
173     apr_hash_t *statCache = (apr_hash_t*)data;
174 	char *key;
175     apr_ssize_t keylen;
176     NXPathCtx_t pathctx;
177 
178     for (hi = apr_hash_first(p, statCache); hi; hi = apr_hash_next(hi)) {
179         apr_hash_this(hi, (const void**)&key, &keylen, (void**)&pathctx);
180 
181         if (pathctx) {
182             NXFreePathContext(pathctx);
183         }
184     }
185 
186     return APR_SUCCESS;
187 }
188 
cstat(NXPathCtx_t ctx,char * path,struct stat * buf,unsigned long requestmap,apr_pool_t * p)189 int cstat (NXPathCtx_t ctx, char *path, struct stat *buf, unsigned long requestmap, apr_pool_t *p)
190 {
191     apr_pool_t *gPool = (apr_pool_t *)getGlobalPool();
192     apr_hash_t *statCache = NULL;
193     apr_thread_rwlock_t *rwlock = NULL;
194 
195     NXPathCtx_t pathctx = 0;
196     char *ptr = NULL, *tr;
197     int len = 0, x;
198     char *ppath;
199     char *pinfo;
200 
201     if (ctx == 1) {
202 
203         /* If there isn't a global pool then just stat the file
204            and return */
205         if (!gPool) {
206             char poolname[50];
207 
208             if (apr_pool_create(&gPool, NULL) != APR_SUCCESS) {
209                 return getstat(ctx, path, buf, requestmap);
210             }
211 
212             setGlobalPool(gPool);
213             apr_pool_tag(gPool, apr_pstrdup(gPool, "cstat_mem_pool"));
214 
215             statCache = apr_hash_make(gPool);
216             apr_pool_userdata_set ((void*)statCache, "STAT_CACHE", stat_cache_cleanup, gPool);
217 
218             apr_thread_rwlock_create(&rwlock, gPool);
219             apr_pool_userdata_set ((void*)rwlock, "STAT_CACHE_LOCK", apr_pool_cleanup_null, gPool);
220         }
221         else {
222             apr_pool_userdata_get((void**)&statCache, "STAT_CACHE", gPool);
223             apr_pool_userdata_get((void**)&rwlock, "STAT_CACHE_LOCK", gPool);
224         }
225 
226         if (!gPool || !statCache || !rwlock) {
227             return getstat(ctx, path, buf, requestmap);
228         }
229 
230         for (x = 0,tr = path;*tr != '\0';tr++,x++) {
231             if (*tr == '\\' || *tr == '/') {
232                 ptr = tr;
233                 len = x;
234             }
235             if (*tr == ':') {
236                 ptr = "\\";
237                 len = x;
238             }
239         }
240 
241         if (ptr) {
242             ppath = apr_pstrndup (p, path, len);
243             strlwr(ppath);
244             if (ptr[1] != '\0') {
245                 ptr++;
246             }
247             /* If the path ended in a trailing slash then our result path
248                will be a single slash. To avoid stat'ing the root with a
249                slash, we need to make sure we stat the current directory
250                with a dot */
251             if (((*ptr == '/') || (*ptr == '\\')) && (*(ptr+1) == '\0')) {
252                 pinfo = apr_pstrdup (p, ".");
253             }
254             else {
255                 pinfo = apr_pstrdup (p, ptr);
256             }
257         }
258 
259         /* If we have a statCache then try to pull the information
260            from the cache.  Otherwise just stat the file and return.*/
261         if (statCache) {
262             apr_thread_rwlock_rdlock(rwlock);
263             pathctx = (NXPathCtx_t) apr_hash_get(statCache, ppath, APR_HASH_KEY_STRING);
264             apr_thread_rwlock_unlock(rwlock);
265             if (pathctx) {
266                 return getstat(pathctx, pinfo, buf, requestmap);
267             }
268             else {
269                 int err;
270 
271                 err = NXCreatePathContext(0, ppath, 0, NULL, &pathctx);
272                 if (!err) {
273                     apr_thread_rwlock_wrlock(rwlock);
274                     apr_hash_set(statCache, apr_pstrdup(gPool,ppath) , APR_HASH_KEY_STRING, (void*)pathctx);
275                     apr_thread_rwlock_unlock(rwlock);
276                     return getstat(pathctx, pinfo, buf, requestmap);
277                 }
278             }
279         }
280     }
281     return getstat(ctx, path, buf, requestmap);
282 }
283 #endif
284 
apr_stat(apr_finfo_t * finfo,const char * fname,apr_int32_t wanted,apr_pool_t * pool)285 APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo,
286                                    const char *fname,
287                                    apr_int32_t wanted, apr_pool_t *pool)
288 {
289     struct stat info;
290     int srv;
291     NXPathCtx_t pathCtx = 0;
292 
293     getcwdpath(NULL, &pathCtx, CTX_ACTUAL_CWD);
294 #ifdef APR_HAS_PSA
295 	srv = getstat(pathCtx, (char*)fname, &info, ST_STAT_BITS|ST_NAME_BIT);
296 #else
297     srv = cstat(pathCtx, (char*)fname, &info, ST_STAT_BITS|ST_NAME_BIT, pool);
298 #endif
299     errno = srv;
300 
301     if (srv == 0) {
302         finfo->pool = pool;
303         finfo->fname = fname;
304         fill_out_finfo(finfo, &info, wanted);
305         if (wanted & APR_FINFO_LINK)
306             wanted &= ~APR_FINFO_LINK;
307         if (wanted & APR_FINFO_NAME) {
308             finfo->name = apr_pstrdup(pool, info.st_name);
309             finfo->valid |= APR_FINFO_NAME;
310         }
311         return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
312     }
313     else {
314 #if !defined(ENOENT) || !defined(ENOTDIR)
315 #error ENOENT || ENOTDIR not defined; please see the
316 #error comments at this line in the source for a workaround.
317         /*
318          * If ENOENT || ENOTDIR is not defined in one of the your OS's
319          * include files, APR cannot report a good reason why the stat()
320          * of the file failed; there are cases where it can fail even though
321          * the file exists.  This opens holes in Apache, for example, because
322          * it becomes possible for someone to get a directory listing of a
323          * directory even though there is an index (eg. index.html) file in
324          * it.  If you do not have a problem with this, delete the above
325          * #error lines and start the compile again.  If you need to do this,
326          * please submit a bug report to http://www.apache.org/bug_report.html
327          * letting us know that you needed to do this.  Please be sure to
328          * include the operating system you are using.
329          */
330         /* WARNING: All errors will be handled as not found
331          */
332 #if !defined(ENOENT)
333         return APR_ENOENT;
334 #else
335         /* WARNING: All errors but not found will be handled as not directory
336          */
337         if (errno != ENOENT)
338             return APR_ENOENT;
339         else
340             return errno;
341 #endif
342 #else /* All was defined well, report the usual: */
343         return errno;
344 #endif
345     }
346 }
347 
apr_file_mtime_set(const char * fname,apr_time_t mtime,apr_pool_t * pool)348 APR_DECLARE(apr_status_t) apr_file_mtime_set(const char *fname,
349                                               apr_time_t mtime,
350                                               apr_pool_t *pool)
351 {
352     apr_status_t status;
353     apr_finfo_t finfo;
354 
355     status = apr_stat(&finfo, fname, APR_FINFO_ATIME, pool);
356     if (status) {
357         return status;
358     }
359 
360 #ifdef HAVE_UTIMES
361     {
362       struct timeval tvp[2];
363 
364       tvp[0].tv_sec = apr_time_sec(finfo.atime);
365       tvp[0].tv_usec = apr_time_usec(finfo.atime);
366       tvp[1].tv_sec = apr_time_sec(mtime);
367       tvp[1].tv_usec = apr_time_usec(mtime);
368 
369       if (utimes(fname, tvp) == -1) {
370         return errno;
371       }
372     }
373 #elif defined(HAVE_UTIME)
374     {
375       struct utimbuf buf;
376 
377       buf.actime = (time_t) (finfo.atime / APR_USEC_PER_SEC);
378       buf.modtime = (time_t) (mtime / APR_USEC_PER_SEC);
379 
380       if (utime(fname, &buf) == -1) {
381         return errno;
382       }
383     }
384 #else
385     return APR_ENOTIMPL;
386 #endif
387 
388     return APR_SUCCESS;
389 }
390