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