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