blob: 4df2134b2bdb022133d66b460b0ef5b33ac030c7 [file] [log] [blame]
Igor Sysoevd92bee52007-09-01 12:11:21 +00001
2/*
3 * Copyright (C) Igor Sysoev
Maxim Konovalovf8d59e32012-01-18 15:07:43 +00004 * Copyright (C) Nginx, Inc.
Igor Sysoevd92bee52007-09-01 12:11:21 +00005 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_event.h>
11
12
13/*
14 * open file cache caches
15 * open file handles with stat() info;
16 * directories stat() info;
17 * files and directories errors: not found, access denied, etc.
18 */
19
20
Igor Sysoev32661712009-09-30 13:21:52 +000021#define NGX_MIN_READ_AHEAD (128 * 1024)
22
23
Igor Sysoevd92bee52007-09-01 12:11:21 +000024static void ngx_open_file_cache_cleanup(void *data);
Andrey Belovbd1e7192012-02-13 16:29:04 +000025#if (NGX_HAVE_OPENAT)
26static ngx_fd_t ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name,
Maxim Dounin04015a42012-02-15 12:17:24 +000027 ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log);
Valentin Bartenev7b373842013-09-02 08:07:59 +040028#if (NGX_HAVE_O_PATH)
29static ngx_int_t ngx_file_o_path_info(ngx_fd_t fd, ngx_file_info_t *fi,
30 ngx_log_t *log);
31#endif
Andrey Belovbd1e7192012-02-13 16:29:04 +000032#endif
33static ngx_fd_t ngx_open_file_wrapper(ngx_str_t *name,
34 ngx_open_file_info_t *of, ngx_int_t mode, ngx_int_t create,
Maxim Dounin04015a42012-02-15 12:17:24 +000035 ngx_int_t access, ngx_log_t *log);
Andrey Belovbd1e7192012-02-13 16:29:04 +000036static ngx_int_t ngx_file_info_wrapper(ngx_str_t *name,
Maxim Dounin04015a42012-02-15 12:17:24 +000037 ngx_open_file_info_t *of, ngx_file_info_t *fi, ngx_log_t *log);
Andrey Belov32c8df42012-02-13 16:16:45 +000038static ngx_int_t ngx_open_and_stat_file(ngx_str_t *name,
39 ngx_open_file_info_t *of, ngx_log_t *log);
Igor Sysoev421a3b82007-12-25 10:46:40 +000040static void ngx_open_file_add_event(ngx_open_file_cache_t *cache,
41 ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log);
Igor Sysoevd92bee52007-09-01 12:11:21 +000042static void ngx_open_file_cleanup(void *data);
43static void ngx_close_cached_file(ngx_open_file_cache_t *cache,
Igor Sysoevf3b0e492007-12-22 13:19:39 +000044 ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log);
Igor Sysoev421a3b82007-12-25 10:46:40 +000045static void ngx_open_file_del_event(ngx_cached_open_file_t *file);
Igor Sysoevd92bee52007-09-01 12:11:21 +000046static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache,
47 ngx_uint_t n, ngx_log_t *log);
48static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
49 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
Igor Sysoev421a3b82007-12-25 10:46:40 +000050static ngx_cached_open_file_t *
51 ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
52 uint32_t hash);
Igor Sysoevd92bee52007-09-01 12:11:21 +000053static void ngx_open_file_cache_remove(ngx_event_t *ev);
54
55
56ngx_open_file_cache_t *
57ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive)
58{
Igor Sysoevd92bee52007-09-01 12:11:21 +000059 ngx_pool_cleanup_t *cln;
60 ngx_open_file_cache_t *cache;
61
62 cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t));
63 if (cache == NULL) {
64 return NULL;
65 }
66
Igor Sysoevddc8cbd2007-12-20 21:29:52 +000067 ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
Igor Sysoev7912e4b2007-12-17 08:52:00 +000068 ngx_open_file_cache_rbtree_insert_value);
Igor Sysoevd92bee52007-09-01 12:11:21 +000069
Igor Sysoevff71b942007-12-21 15:33:15 +000070 ngx_queue_init(&cache->expire_queue);
71
Igor Sysoevd92bee52007-09-01 12:11:21 +000072 cache->current = 0;
73 cache->max = max;
74 cache->inactive = inactive;
75
76 cln = ngx_pool_cleanup_add(pool, 0);
77 if (cln == NULL) {
78 return NULL;
79 }
80
81 cln->handler = ngx_open_file_cache_cleanup;
82 cln->data = cache;
83
84 return cache;
85}
86
87
88static void
89ngx_open_file_cache_cleanup(void *data)
90{
91 ngx_open_file_cache_t *cache = data;
92
Igor Sysoevff71b942007-12-21 15:33:15 +000093 ngx_queue_t *q;
Igor Sysoevd92bee52007-09-01 12:11:21 +000094 ngx_cached_open_file_t *file;
95
96 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
97 "open file cache cleanup");
98
99 for ( ;; ) {
100
Igor Sysoevff71b942007-12-21 15:33:15 +0000101 if (ngx_queue_empty(&cache->expire_queue)) {
Igor Sysoevf2d60af2007-12-21 16:19:14 +0000102 break;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000103 }
104
Igor Sysoevff71b942007-12-21 15:33:15 +0000105 q = ngx_queue_last(&cache->expire_queue);
106
107 file = ngx_queue_data(q, ngx_cached_open_file_t, queue);
108
109 ngx_queue_remove(q);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000110
111 ngx_rbtree_delete(&cache->rbtree, &file->node);
112
113 cache->current--;
114
115 ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
116 "delete cached open file: %s", file->name);
117
118 if (!file->err && !file->is_dir) {
119 file->close = 1;
120 file->count = 0;
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000121 ngx_close_cached_file(cache, file, 0, ngx_cycle->log);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000122
123 } else {
124 ngx_free(file->name);
125 ngx_free(file);
126 }
127 }
128
129 if (cache->current) {
130 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
Sergey Kandaurove09741b2013-08-20 20:47:16 +0400131 "%ui items still leave in open file cache",
Igor Sysoevd92bee52007-09-01 12:11:21 +0000132 cache->current);
133 }
134
135 if (cache->rbtree.root != cache->rbtree.sentinel) {
136 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
137 "rbtree still is not empty in open file cache");
138
139 }
140}
141
142
143ngx_int_t
144ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
145 ngx_open_file_info_t *of, ngx_pool_t *pool)
146{
147 time_t now;
148 uint32_t hash;
149 ngx_int_t rc;
Igor Sysoevf1cc4572009-04-27 09:55:53 +0000150 ngx_file_info_t fi;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000151 ngx_pool_cleanup_t *cln;
152 ngx_cached_open_file_t *file;
153 ngx_pool_cleanup_file_t *clnf;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000154 ngx_open_file_cache_cleanup_t *ofcln;
155
Igor Sysoevd6711d32008-06-26 16:10:13 +0000156 of->fd = NGX_INVALID_FILE;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000157 of->err = 0;
158
159 if (cache == NULL) {
160
Igor Sysoevf1cc4572009-04-27 09:55:53 +0000161 if (of->test_only) {
162
Maxim Dounin04015a42012-02-15 12:17:24 +0000163 if (ngx_file_info_wrapper(name, of, &fi, pool->log)
164 == NGX_FILE_ERROR)
165 {
Igor Sysoevf1cc4572009-04-27 09:55:53 +0000166 return NGX_ERROR;
167 }
168
169 of->uniq = ngx_file_uniq(&fi);
170 of->mtime = ngx_file_mtime(&fi);
171 of->size = ngx_file_size(&fi);
Igor Sysoevef1f33b2011-04-22 10:06:43 +0000172 of->fs_size = ngx_file_fs_size(&fi);
Igor Sysoevf1cc4572009-04-27 09:55:53 +0000173 of->is_dir = ngx_is_dir(&fi);
174 of->is_file = ngx_is_file(&fi);
175 of->is_link = ngx_is_link(&fi);
176 of->is_exec = ngx_is_exec(&fi);
177
178 return NGX_OK;
179 }
180
Igor Sysoevd92bee52007-09-01 12:11:21 +0000181 cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
182 if (cln == NULL) {
183 return NGX_ERROR;
184 }
185
Andrey Belov32c8df42012-02-13 16:16:45 +0000186 rc = ngx_open_and_stat_file(name, of, pool->log);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000187
188 if (rc == NGX_OK && !of->is_dir) {
189 cln->handler = ngx_pool_cleanup_file;
190 clnf = cln->data;
191
192 clnf->fd = of->fd;
193 clnf->name = name->data;
194 clnf->log = pool->log;
195 }
196
197 return rc;
198 }
199
200 cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t));
201 if (cln == NULL) {
202 return NGX_ERROR;
203 }
204
Igor Sysoevd92bee52007-09-01 12:11:21 +0000205 now = ngx_time();
206
Igor Sysoev421a3b82007-12-25 10:46:40 +0000207 hash = ngx_crc32_long(name->data, name->len);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000208
Igor Sysoev421a3b82007-12-25 10:46:40 +0000209 file = ngx_open_file_lookup(cache, name, hash);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000210
Igor Sysoev421a3b82007-12-25 10:46:40 +0000211 if (file) {
Igor Sysoevd92bee52007-09-01 12:11:21 +0000212
Igor Sysoev421a3b82007-12-25 10:46:40 +0000213 file->uses++;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000214
Igor Sysoevada91902008-04-29 18:14:45 +0000215 ngx_queue_remove(&file->queue);
216
Igor Sysoev421a3b82007-12-25 10:46:40 +0000217 if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) {
Igor Sysoevd92bee52007-09-01 12:11:21 +0000218
Igor Sysoev421a3b82007-12-25 10:46:40 +0000219 /* file was not used often enough to keep open */
Igor Sysoevd92bee52007-09-01 12:11:21 +0000220
Andrey Belov32c8df42012-02-13 16:16:45 +0000221 rc = ngx_open_and_stat_file(name, of, pool->log);
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000222
Igor Sysoev421a3b82007-12-25 10:46:40 +0000223 if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
224 goto failed;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000225 }
226
Igor Sysoev421a3b82007-12-25 10:46:40 +0000227 goto add_event;
228 }
Igor Sysoevd92bee52007-09-01 12:11:21 +0000229
Igor Sysoevbf934762008-06-30 12:11:47 +0000230 if (file->use_event
Igor Sysoev3e6f74d2008-06-23 13:35:34 +0000231 || (file->event == NULL
232 && (of->uniq == 0 || of->uniq == file->uniq)
Andrey Belovbd1e7192012-02-13 16:29:04 +0000233 && now - file->created < of->valid
234#if (NGX_HAVE_OPENAT)
235 && of->disable_symlinks == file->disable_symlinks
Valentin Bartenev34679112012-02-27 16:46:57 +0000236 && of->disable_symlinks_from == file->disable_symlinks_from
Andrey Belovbd1e7192012-02-13 16:29:04 +0000237#endif
238 ))
Igor Sysoev421a3b82007-12-25 10:46:40 +0000239 {
240 if (file->err == 0) {
Igor Sysoevd92bee52007-09-01 12:11:21 +0000241
Igor Sysoev421a3b82007-12-25 10:46:40 +0000242 of->fd = file->fd;
243 of->uniq = file->uniq;
244 of->mtime = file->mtime;
245 of->size = file->size;
246
247 of->is_dir = file->is_dir;
248 of->is_file = file->is_file;
249 of->is_link = file->is_link;
250 of->is_exec = file->is_exec;
Igor Sysoev77cdae12008-09-12 13:39:51 +0000251 of->is_directio = file->is_directio;
Igor Sysoev421a3b82007-12-25 10:46:40 +0000252
253 if (!file->is_dir) {
254 file->count++;
255 ngx_open_file_add_event(cache, file, of, pool->log);
256 }
257
258 } else {
259 of->err = file->err;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000260#if (NGX_HAVE_OPENAT)
261 of->failed = file->disable_symlinks ? ngx_openat_file_n
262 : ngx_open_file_n;
263#else
Igor Sysoev5461f392009-04-30 08:01:50 +0000264 of->failed = ngx_open_file_n;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000265#endif
Igor Sysoev421a3b82007-12-25 10:46:40 +0000266 }
267
268 goto found;
269 }
270
271 ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
272 "retest open file: %s, fd:%d, c:%d, e:%d",
273 file->name, file->fd, file->count, file->err);
274
275 if (file->is_dir) {
276
277 /*
278 * chances that directory became file are very small
279 * so test_dir flag allows to use a single syscall
280 * in ngx_file_info() instead of three syscalls
281 */
282
283 of->test_dir = 1;
284 }
285
Igor Sysoevd6711d32008-06-26 16:10:13 +0000286 of->fd = file->fd;
287 of->uniq = file->uniq;
288
Andrey Belov32c8df42012-02-13 16:16:45 +0000289 rc = ngx_open_and_stat_file(name, of, pool->log);
Igor Sysoev421a3b82007-12-25 10:46:40 +0000290
291 if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
292 goto failed;
293 }
294
295 if (of->is_dir) {
296
297 if (file->is_dir || file->err) {
298 goto update;
299 }
300
301 /* file became directory */
302
303 } else if (of->err == 0) { /* file */
304
305 if (file->is_dir || file->err) {
306 goto add_event;
307 }
308
Igor Sysoevbf934762008-06-30 12:11:47 +0000309 if (of->uniq == file->uniq) {
Igor Sysoev421a3b82007-12-25 10:46:40 +0000310
Igor Sysoev421a3b82007-12-25 10:46:40 +0000311 if (file->event) {
312 file->use_event = 1;
Igor Sysoev421a3b82007-12-25 10:46:40 +0000313 }
314
Igor Sysoev9c5d2512011-09-14 14:28:55 +0000315 of->is_directio = file->is_directio;
316
Igor Sysoev7ffb73c2011-09-14 14:12:35 +0000317 goto update;
Igor Sysoev421a3b82007-12-25 10:46:40 +0000318 }
319
320 /* file was changed */
321
322 } else { /* error to cache */
323
324 if (file->err || file->is_dir) {
325 goto update;
326 }
327
328 /* file was removed, etc. */
329 }
330
331 if (file->count == 0) {
332
333 ngx_open_file_del_event(file);
334
335 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
336 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
Andrey Belov32c8df42012-02-13 16:16:45 +0000337 ngx_close_file_n " \"%V\" failed", name);
Igor Sysoev421a3b82007-12-25 10:46:40 +0000338 }
339
340 goto add_event;
341 }
342
343 ngx_rbtree_delete(&cache->rbtree, &file->node);
344
345 cache->current--;
346
347 file->close = 1;
348
349 goto create;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000350 }
351
352 /* not found */
353
Andrey Belov32c8df42012-02-13 16:16:45 +0000354 rc = ngx_open_and_stat_file(name, of, pool->log);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000355
356 if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
357 goto failed;
358 }
359
360create:
361
362 if (cache->current >= cache->max) {
363 ngx_expire_old_cached_files(cache, 0, pool->log);
364 }
365
366 file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log);
367
368 if (file == NULL) {
369 goto failed;
370 }
371
372 file->name = ngx_alloc(name->len + 1, pool->log);
373
374 if (file->name == NULL) {
375 ngx_free(file);
376 file = NULL;
377 goto failed;
378 }
379
380 ngx_cpystrn(file->name, name->data, name->len + 1);
381
382 file->node.key = hash;
383
384 ngx_rbtree_insert(&cache->rbtree, &file->node);
385
386 cache->current++;
387
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000388 file->uses = 1;
Igor Sysoev421a3b82007-12-25 10:46:40 +0000389 file->count = 0;
Igor Sysoev282109b2009-06-12 14:23:29 +0000390 file->use_event = 0;
Igor Sysoev421a3b82007-12-25 10:46:40 +0000391 file->event = NULL;
392
393add_event:
394
395 ngx_open_file_add_event(cache, file, of, pool->log);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000396
397update:
398
Igor Sysoevd92bee52007-09-01 12:11:21 +0000399 file->fd = of->fd;
400 file->err = of->err;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000401#if (NGX_HAVE_OPENAT)
402 file->disable_symlinks = of->disable_symlinks;
Valentin Bartenev34679112012-02-27 16:46:57 +0000403 file->disable_symlinks_from = of->disable_symlinks_from;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000404#endif
Igor Sysoevd92bee52007-09-01 12:11:21 +0000405
406 if (of->err == 0) {
407 file->uniq = of->uniq;
408 file->mtime = of->mtime;
409 file->size = of->size;
410
411 file->close = 0;
412
413 file->is_dir = of->is_dir;
414 file->is_file = of->is_file;
415 file->is_link = of->is_link;
416 file->is_exec = of->is_exec;
Igor Sysoev77cdae12008-09-12 13:39:51 +0000417 file->is_directio = of->is_directio;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000418
419 if (!of->is_dir) {
420 file->count++;
421 }
422 }
423
Igor Sysoevd92bee52007-09-01 12:11:21 +0000424 file->created = now;
425
426found:
427
428 file->accessed = now;
429
Igor Sysoevff71b942007-12-21 15:33:15 +0000430 ngx_queue_insert_head(&cache->expire_queue, &file->queue);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000431
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000432 ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0,
433 "cached open file: %s, fd:%d, c:%d, e:%d, u:%d",
434 file->name, file->fd, file->count, file->err, file->uses);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000435
436 if (of->err == 0) {
437
438 if (!of->is_dir) {
439 cln->handler = ngx_open_file_cleanup;
440 ofcln = cln->data;
441
442 ofcln->cache = cache;
443 ofcln->file = file;
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000444 ofcln->min_uses = of->min_uses;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000445 ofcln->log = pool->log;
446 }
447
448 return NGX_OK;
449 }
450
451 return NGX_ERROR;
452
453failed:
454
Igor Sysoeva3278412008-04-29 18:15:23 +0000455 if (file) {
Igor Sysoevd92bee52007-09-01 12:11:21 +0000456 ngx_rbtree_delete(&cache->rbtree, &file->node);
457
458 cache->current--;
459
Igor Sysoeva3278412008-04-29 18:15:23 +0000460 if (file->count == 0) {
Igor Sysoevd92bee52007-09-01 12:11:21 +0000461
Igor Sysoev433608c2008-05-14 07:54:52 +0000462 if (file->fd != NGX_INVALID_FILE) {
463 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
464 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
465 ngx_close_file_n " \"%s\" failed",
Igor Sysoeva3278412008-04-29 18:15:23 +0000466 file->name);
Igor Sysoev433608c2008-05-14 07:54:52 +0000467 }
468 }
Igor Sysoeva3278412008-04-29 18:15:23 +0000469
470 ngx_free(file->name);
471 ngx_free(file);
472
473 } else {
474 file->close = 1;
475 }
Igor Sysoevd92bee52007-09-01 12:11:21 +0000476 }
477
478 if (of->fd != NGX_INVALID_FILE) {
479 if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
480 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
Andrey Belov32c8df42012-02-13 16:16:45 +0000481 ngx_close_file_n " \"%V\" failed", name);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000482 }
483 }
484
485 return NGX_ERROR;
486}
487
488
Andrey Belovbd1e7192012-02-13 16:29:04 +0000489#if (NGX_HAVE_OPENAT)
490
491static ngx_fd_t
492ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name,
Maxim Dounin04015a42012-02-15 12:17:24 +0000493 ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log)
Andrey Belovbd1e7192012-02-13 16:29:04 +0000494{
495 ngx_fd_t fd;
Maxim Dounin04015a42012-02-15 12:17:24 +0000496 ngx_err_t err;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000497 ngx_file_info_t fi, atfi;
498
499 /*
500 * To allow symlinks with the same owner, use openat() (followed
501 * by fstat()) and fstatat(AT_SYMLINK_NOFOLLOW), and then compare
502 * uids between fstat() and fstatat().
503 *
504 * As there is a race between openat() and fstatat() we don't
505 * know if openat() in fact opened symlink or not. Therefore,
506 * we have to compare uids even if fstatat() reports the opened
507 * component isn't a symlink (as we don't know whether it was
508 * symlink during openat() or not).
509 */
510
511 fd = ngx_openat_file(at_fd, name, mode, create, access);
512
Valentin Bartenev8c27e642012-02-21 15:01:25 +0000513 if (fd == NGX_INVALID_FILE) {
514 return NGX_INVALID_FILE;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000515 }
516
517 if (ngx_file_at_info(at_fd, name, &atfi, AT_SYMLINK_NOFOLLOW)
518 == NGX_FILE_ERROR)
519 {
Maxim Dounin04015a42012-02-15 12:17:24 +0000520 err = ngx_errno;
521 goto failed;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000522 }
523
Valentin Bartenev7b373842013-09-02 08:07:59 +0400524#if (NGX_HAVE_O_PATH)
525 if (ngx_file_o_path_info(fd, &fi, log) == NGX_ERROR) {
526 err = ngx_errno;
527 goto failed;
528 }
529#else
Andrey Belovbd1e7192012-02-13 16:29:04 +0000530 if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
Maxim Dounin04015a42012-02-15 12:17:24 +0000531 err = ngx_errno;
532 goto failed;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000533 }
Valentin Bartenev7b373842013-09-02 08:07:59 +0400534#endif
Andrey Belovbd1e7192012-02-13 16:29:04 +0000535
536 if (fi.st_uid != atfi.st_uid) {
Maxim Dounin04015a42012-02-15 12:17:24 +0000537 err = NGX_ELOOP;
538 goto failed;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000539 }
540
541 return fd;
Maxim Dounin04015a42012-02-15 12:17:24 +0000542
543failed:
544
545 if (ngx_close_file(fd) == NGX_FILE_ERROR) {
546 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
547 ngx_close_file_n " \"%V\" failed", name);
548 }
549
550 ngx_set_errno(err);
551
552 return NGX_INVALID_FILE;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000553}
554
Valentin Bartenev7b373842013-09-02 08:07:59 +0400555
556#if (NGX_HAVE_O_PATH)
557
558static ngx_int_t
559ngx_file_o_path_info(ngx_fd_t fd, ngx_file_info_t *fi, ngx_log_t *log)
560{
561 static ngx_uint_t use_fstat = 1;
562
563 /*
564 * In Linux 2.6.39 the O_PATH flag was introduced that allows to obtain
565 * a descriptor without actually opening file or directory. It requires
566 * less permissions for path components, but till Linux 3.6 fstat() returns
567 * EBADF on such descriptors, and fstatat() with the AT_EMPTY_PATH flag
568 * should be used instead.
569 *
570 * Three scenarios are handled in this function:
571 *
572 * 1) The kernel is newer than 3.6 or fstat() with O_PATH support was
573 * backported by vendor. Then fstat() is used.
574 *
575 * 2) The kernel is newer than 2.6.39 but older than 3.6. In this case
576 * the first call of fstat() returns EBADF and we fallback to fstatat()
577 * with AT_EMPTY_PATH which was introduced at the same time as O_PATH.
578 *
579 * 3) The kernel is older than 2.6.39 but nginx was build with O_PATH
580 * support. Since descriptors are opened with O_PATH|O_RDONLY flags
581 * and O_PATH is ignored by the kernel then the O_RDONLY flag is
582 * actually used. In this case fstat() just works.
583 */
584
585 if (use_fstat) {
586 if (ngx_fd_info(fd, fi) != NGX_FILE_ERROR) {
587 return NGX_OK;
588 }
589
590 if (ngx_errno != NGX_EBADF) {
591 return NGX_ERROR;
592 }
593
594 ngx_log_error(NGX_LOG_NOTICE, log, 0,
595 "fstat(O_PATH) failed with EBADF, "
596 "switching to fstatat(AT_EMPTY_PATH)");
597
598 use_fstat = 0;
Valentin Bartenev7b373842013-09-02 08:07:59 +0400599 }
600
601 if (ngx_file_at_info(fd, "", fi, AT_EMPTY_PATH) != NGX_FILE_ERROR) {
602 return NGX_OK;
603 }
604
605 return NGX_ERROR;
606}
607
Andrey Belovbd1e7192012-02-13 16:29:04 +0000608#endif
609
Valentin Bartenev7b373842013-09-02 08:07:59 +0400610#endif /* NGX_HAVE_OPENAT */
611
Andrey Belovbd1e7192012-02-13 16:29:04 +0000612
613static ngx_fd_t
614ngx_open_file_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,
Maxim Dounin04015a42012-02-15 12:17:24 +0000615 ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log)
Andrey Belovbd1e7192012-02-13 16:29:04 +0000616{
617 ngx_fd_t fd;
618
619#if !(NGX_HAVE_OPENAT)
620
621 fd = ngx_open_file(name->data, mode, create, access);
622
Maxim Dounin04015a42012-02-15 12:17:24 +0000623 if (fd == NGX_INVALID_FILE) {
Andrey Belovbd1e7192012-02-13 16:29:04 +0000624 of->err = ngx_errno;
625 of->failed = ngx_open_file_n;
Maxim Dounin04015a42012-02-15 12:17:24 +0000626 return NGX_INVALID_FILE;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000627 }
628
629 return fd;
630
631#else
632
Maxim Dounin32b000b2012-02-15 12:18:55 +0000633 u_char *p, *cp, *end;
634 ngx_fd_t at_fd;
635 ngx_str_t at_name;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000636
637 if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {
638 fd = ngx_open_file(name->data, mode, create, access);
639
Maxim Dounin04015a42012-02-15 12:17:24 +0000640 if (fd == NGX_INVALID_FILE) {
Andrey Belovbd1e7192012-02-13 16:29:04 +0000641 of->err = ngx_errno;
642 of->failed = ngx_open_file_n;
Maxim Dounin04015a42012-02-15 12:17:24 +0000643 return NGX_INVALID_FILE;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000644 }
645
646 return fd;
647 }
648
Maxim Dounin32b000b2012-02-15 12:18:55 +0000649 p = name->data;
650 end = p + name->len;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000651
Maxim Dounin04015a42012-02-15 12:17:24 +0000652 at_name = *name;
Maxim Dounin04015a42012-02-15 12:17:24 +0000653
Valentin Bartenev34679112012-02-27 16:46:57 +0000654 if (of->disable_symlinks_from) {
655
656 cp = p + of->disable_symlinks_from;
657
658 *cp = '\0';
659
660 at_fd = ngx_open_file(p, NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
661 NGX_FILE_OPEN, 0);
662
663 *cp = '/';
664
665 if (at_fd == NGX_INVALID_FILE) {
666 of->err = ngx_errno;
667 of->failed = ngx_open_file_n;
668 return NGX_INVALID_FILE;
669 }
670
671 at_name.len = of->disable_symlinks_from;
672 p = cp + 1;
673
674 } else if (*p == '/') {
675
Valentin Bartenev8c27e642012-02-21 15:01:25 +0000676 at_fd = ngx_open_file("/",
Valentin Bartenev86c55132012-02-21 15:10:13 +0000677 NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
Valentin Bartenev8c27e642012-02-21 15:01:25 +0000678 NGX_FILE_OPEN, 0);
Maxim Dounin32b000b2012-02-15 12:18:55 +0000679
Maxim Dounin6bb86e32012-02-20 19:14:07 +0000680 if (at_fd == NGX_INVALID_FILE) {
Maxim Dounin32b000b2012-02-15 12:18:55 +0000681 of->err = ngx_errno;
682 of->failed = ngx_openat_file_n;
Maxim Dounin6bb86e32012-02-20 19:14:07 +0000683 return NGX_INVALID_FILE;
Maxim Dounin32b000b2012-02-15 12:18:55 +0000684 }
685
686 at_name.len = 1;
687 p++;
Valentin Bartenev8c27e642012-02-21 15:01:25 +0000688
689 } else {
690 at_fd = NGX_AT_FDCWD;
Maxim Dounin32b000b2012-02-15 12:18:55 +0000691 }
Andrey Belovbd1e7192012-02-13 16:29:04 +0000692
693 for ( ;; ) {
694 cp = ngx_strlchr(p, end, '/');
695 if (cp == NULL) {
696 break;
697 }
698
Maxim Dounin32b000b2012-02-15 12:18:55 +0000699 if (cp == p) {
700 p++;
701 continue;
702 }
703
Andrey Belovbd1e7192012-02-13 16:29:04 +0000704 *cp = '\0';
705
706 if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) {
707 fd = ngx_openat_file_owner(at_fd, p,
Valentin Bartenev86c55132012-02-21 15:10:13 +0000708 NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
Maxim Dounin04015a42012-02-15 12:17:24 +0000709 NGX_FILE_OPEN, 0, log);
Andrey Belovbd1e7192012-02-13 16:29:04 +0000710
711 } else {
712 fd = ngx_openat_file(at_fd, p,
Valentin Bartenev86c55132012-02-21 15:10:13 +0000713 NGX_FILE_SEARCH|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW,
Andrey Belovbd1e7192012-02-13 16:29:04 +0000714 NGX_FILE_OPEN, 0);
715 }
716
717 *cp = '/';
718
Maxim Dounin04015a42012-02-15 12:17:24 +0000719 if (fd == NGX_INVALID_FILE) {
Andrey Belovbd1e7192012-02-13 16:29:04 +0000720 of->err = ngx_errno;
721 of->failed = ngx_openat_file_n;
Maxim Dounin04015a42012-02-15 12:17:24 +0000722 goto failed;
723 }
724
Maxim Dounin7ba66f42012-02-20 19:14:35 +0000725 if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {
Maxim Dounin04015a42012-02-15 12:17:24 +0000726 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
Maxim Dounin6bb86e32012-02-20 19:14:07 +0000727 ngx_close_file_n " \"%V\" failed", &at_name);
Andrey Belovbd1e7192012-02-13 16:29:04 +0000728 }
729
730 p = cp + 1;
731 at_fd = fd;
Maxim Dounin04015a42012-02-15 12:17:24 +0000732 at_name.len = cp - at_name.data;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000733 }
734
Valentin Bartenev8c27e642012-02-21 15:01:25 +0000735 if (p == end) {
Maxim Dounin32b000b2012-02-15 12:18:55 +0000736
737 /*
Valentin Bartenev86c55132012-02-21 15:10:13 +0000738 * If pathname ends with a trailing slash, assume the last path
739 * component is a directory and reopen it with requested flags;
740 * if not, fail with ENOTDIR as per POSIX.
Maxim Dounin32b000b2012-02-15 12:18:55 +0000741 *
Valentin Bartenev86c55132012-02-21 15:10:13 +0000742 * We cannot rely on O_DIRECTORY in the loop above to check
743 * that the last path component is a directory because
744 * O_DIRECTORY doesn't work on FreeBSD 8. Fortunately, by
745 * reopening a directory, we don't depend on it at all.
Maxim Dounin32b000b2012-02-15 12:18:55 +0000746 */
747
Valentin Bartenev86c55132012-02-21 15:10:13 +0000748 fd = ngx_openat_file(at_fd, ".", mode, create, access);
749 goto done;
Maxim Dounin32b000b2012-02-15 12:18:55 +0000750 }
751
Valentin Bartenev15b31732012-02-21 15:04:41 +0000752 if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER
753 && !(create & (NGX_FILE_CREATE_OR_OPEN|NGX_FILE_TRUNCATE)))
754 {
Maxim Dounin04015a42012-02-15 12:17:24 +0000755 fd = ngx_openat_file_owner(at_fd, p, mode, create, access, log);
Andrey Belovbd1e7192012-02-13 16:29:04 +0000756
757 } else {
758 fd = ngx_openat_file(at_fd, p, mode|NGX_FILE_NOFOLLOW, create, access);
759 }
760
Valentin Bartenev86c55132012-02-21 15:10:13 +0000761done:
762
Maxim Dounin04015a42012-02-15 12:17:24 +0000763 if (fd == NGX_INVALID_FILE) {
Andrey Belovbd1e7192012-02-13 16:29:04 +0000764 of->err = ngx_errno;
765 of->failed = ngx_openat_file_n;
766 }
767
Maxim Dounin04015a42012-02-15 12:17:24 +0000768failed:
769
Maxim Dounin7ba66f42012-02-20 19:14:35 +0000770 if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {
Maxim Dounin04015a42012-02-15 12:17:24 +0000771 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
Maxim Dounin6bb86e32012-02-20 19:14:07 +0000772 ngx_close_file_n " \"%V\" failed", &at_name);
Maxim Dounin04015a42012-02-15 12:17:24 +0000773 }
Andrey Belovbd1e7192012-02-13 16:29:04 +0000774
775 return fd;
776#endif
777}
778
779
780static ngx_int_t
781ngx_file_info_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,
Maxim Dounin04015a42012-02-15 12:17:24 +0000782 ngx_file_info_t *fi, ngx_log_t *log)
Andrey Belovbd1e7192012-02-13 16:29:04 +0000783{
784 ngx_int_t rc;
785
786#if !(NGX_HAVE_OPENAT)
787
788 rc = ngx_file_info(name->data, fi);
789
790 if (rc == NGX_FILE_ERROR) {
791 of->err = ngx_errno;
792 of->failed = ngx_file_info_n;
793 return NGX_FILE_ERROR;
794 }
795
796 return rc;
797
798#else
799
800 ngx_fd_t fd;
801
802 if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {
803
804 rc = ngx_file_info(name->data, fi);
805
806 if (rc == NGX_FILE_ERROR) {
807 of->err = ngx_errno;
808 of->failed = ngx_file_info_n;
809 return NGX_FILE_ERROR;
810 }
811
812 return rc;
813 }
814
815 fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
Maxim Dounin04015a42012-02-15 12:17:24 +0000816 NGX_FILE_OPEN, 0, log);
Andrey Belovbd1e7192012-02-13 16:29:04 +0000817
Maxim Dounin04015a42012-02-15 12:17:24 +0000818 if (fd == NGX_INVALID_FILE) {
Andrey Belovbd1e7192012-02-13 16:29:04 +0000819 return NGX_FILE_ERROR;
820 }
821
Maxim Dounin04015a42012-02-15 12:17:24 +0000822 rc = ngx_fd_info(fd, fi);
823
824 if (rc == NGX_FILE_ERROR) {
Andrey Belovbd1e7192012-02-13 16:29:04 +0000825 of->err = ngx_errno;
826 of->failed = ngx_fd_info_n;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000827 }
828
Maxim Dounin04015a42012-02-15 12:17:24 +0000829 if (ngx_close_file(fd) == NGX_FILE_ERROR) {
830 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
831 ngx_close_file_n " \"%V\" failed", name);
832 }
Andrey Belovbd1e7192012-02-13 16:29:04 +0000833
Maxim Dounin04015a42012-02-15 12:17:24 +0000834 return rc;
Andrey Belovbd1e7192012-02-13 16:29:04 +0000835#endif
836}
837
838
Igor Sysoevd92bee52007-09-01 12:11:21 +0000839static ngx_int_t
Andrey Belov32c8df42012-02-13 16:16:45 +0000840ngx_open_and_stat_file(ngx_str_t *name, ngx_open_file_info_t *of,
841 ngx_log_t *log)
Igor Sysoevd92bee52007-09-01 12:11:21 +0000842{
843 ngx_fd_t fd;
844 ngx_file_info_t fi;
845
Igor Sysoevbf934762008-06-30 12:11:47 +0000846 if (of->fd != NGX_INVALID_FILE) {
Igor Sysoevd92bee52007-09-01 12:11:21 +0000847
Maxim Dounin04015a42012-02-15 12:17:24 +0000848 if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {
Andrey Belovbd1e7192012-02-13 16:29:04 +0000849 of->fd = NGX_INVALID_FILE;
850 return NGX_ERROR;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000851 }
852
Igor Sysoevbf934762008-06-30 12:11:47 +0000853 if (of->uniq == ngx_file_uniq(&fi)) {
Igor Sysoevd6711d32008-06-26 16:10:13 +0000854 goto done;
855 }
Igor Sysoevd92bee52007-09-01 12:11:21 +0000856
Igor Sysoevbf934762008-06-30 12:11:47 +0000857 } else if (of->test_dir) {
858
Maxim Dounin04015a42012-02-15 12:17:24 +0000859 if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {
Andrey Belovbd1e7192012-02-13 16:29:04 +0000860 of->fd = NGX_INVALID_FILE;
861 return NGX_ERROR;
Igor Sysoevbf934762008-06-30 12:11:47 +0000862 }
863
Igor Sysoev467f4372009-01-21 15:50:52 +0000864 if (ngx_is_dir(&fi)) {
Igor Sysoevd6711d32008-06-26 16:10:13 +0000865 goto done;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000866 }
867 }
868
Igor Sysoevb8821542008-06-30 12:27:24 +0000869 if (!of->log) {
Igor Sysoevbfc8b782010-04-21 15:59:36 +0000870
871 /*
872 * Use non-blocking open() not to hang on FIFO files, etc.
873 * This flag has no effect on a regular files.
874 */
875
Andrey Belovbd1e7192012-02-13 16:29:04 +0000876 fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
Maxim Dounin04015a42012-02-15 12:17:24 +0000877 NGX_FILE_OPEN, 0, log);
Igor Sysoevb8821542008-06-30 12:27:24 +0000878
879 } else {
Andrey Belovbd1e7192012-02-13 16:29:04 +0000880 fd = ngx_open_file_wrapper(name, of, NGX_FILE_APPEND,
881 NGX_FILE_CREATE_OR_OPEN,
Maxim Dounin04015a42012-02-15 12:17:24 +0000882 NGX_FILE_DEFAULT_ACCESS, log);
Igor Sysoevb8821542008-06-30 12:27:24 +0000883 }
Igor Sysoevd92bee52007-09-01 12:11:21 +0000884
885 if (fd == NGX_INVALID_FILE) {
Andrey Belovbd1e7192012-02-13 16:29:04 +0000886 of->fd = NGX_INVALID_FILE;
887 return NGX_ERROR;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000888 }
889
890 if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
891 ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
Andrey Belov32c8df42012-02-13 16:16:45 +0000892 ngx_fd_info_n " \"%V\" failed", name);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000893
894 if (ngx_close_file(fd) == NGX_FILE_ERROR) {
895 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
Andrey Belov32c8df42012-02-13 16:16:45 +0000896 ngx_close_file_n " \"%V\" failed", name);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000897 }
898
Igor Sysoevd6711d32008-06-26 16:10:13 +0000899 of->fd = NGX_INVALID_FILE;
900
Igor Sysoevd92bee52007-09-01 12:11:21 +0000901 return NGX_ERROR;
902 }
903
904 if (ngx_is_dir(&fi)) {
905 if (ngx_close_file(fd) == NGX_FILE_ERROR) {
906 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
Andrey Belov32c8df42012-02-13 16:16:45 +0000907 ngx_close_file_n " \"%V\" failed", name);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000908 }
909
Igor Sysoevd6711d32008-06-26 16:10:13 +0000910 of->fd = NGX_INVALID_FILE;
911
912 } else {
913 of->fd = fd;
Igor Sysoev385af282008-07-30 12:34:04 +0000914
Igor Sysoev32661712009-09-30 13:21:52 +0000915 if (of->read_ahead && ngx_file_size(&fi) > NGX_MIN_READ_AHEAD) {
916 if (ngx_read_ahead(fd, of->read_ahead) == NGX_ERROR) {
917 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
Andrey Belov32c8df42012-02-13 16:16:45 +0000918 ngx_read_ahead_n " \"%V\" failed", name);
Igor Sysoev32661712009-09-30 13:21:52 +0000919 }
920 }
921
Igor Sysoev385af282008-07-30 12:34:04 +0000922 if (of->directio <= ngx_file_size(&fi)) {
Igor Sysoev48a28e22009-09-25 14:17:28 +0000923 if (ngx_directio_on(fd) == NGX_FILE_ERROR) {
Igor Sysoev385af282008-07-30 12:34:04 +0000924 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
Andrey Belov32c8df42012-02-13 16:16:45 +0000925 ngx_directio_on_n " \"%V\" failed", name);
Igor Sysoev8633e1f2008-09-05 14:48:47 +0000926
927 } else {
928 of->is_directio = 1;
Igor Sysoev385af282008-07-30 12:34:04 +0000929 }
930 }
Igor Sysoevd92bee52007-09-01 12:11:21 +0000931 }
932
Igor Sysoevd6711d32008-06-26 16:10:13 +0000933done:
934
Igor Sysoevd92bee52007-09-01 12:11:21 +0000935 of->uniq = ngx_file_uniq(&fi);
936 of->mtime = ngx_file_mtime(&fi);
937 of->size = ngx_file_size(&fi);
Igor Sysoevef1f33b2011-04-22 10:06:43 +0000938 of->fs_size = ngx_file_fs_size(&fi);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000939 of->is_dir = ngx_is_dir(&fi);
940 of->is_file = ngx_is_file(&fi);
941 of->is_link = ngx_is_link(&fi);
942 of->is_exec = ngx_is_exec(&fi);
943
944 return NGX_OK;
945}
946
947
Igor Sysoev421a3b82007-12-25 10:46:40 +0000948/*
949 * we ignore any possible event setting error and
950 * fallback to usual periodic file retests
951 */
952
953static void
954ngx_open_file_add_event(ngx_open_file_cache_t *cache,
955 ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log)
956{
957 ngx_open_file_cache_event_t *fev;
958
959 if (!(ngx_event_flags & NGX_USE_VNODE_EVENT)
960 || !of->events
961 || file->event
962 || of->fd == NGX_INVALID_FILE
963 || file->uses < of->min_uses)
964 {
965 return;
966 }
967
Igor Sysoevbf934762008-06-30 12:11:47 +0000968 file->use_event = 0;
969
Igor Sysoev421a3b82007-12-25 10:46:40 +0000970 file->event = ngx_calloc(sizeof(ngx_event_t), log);
971 if (file->event== NULL) {
972 return;
973 }
974
975 fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log);
976 if (fev == NULL) {
977 ngx_free(file->event);
978 file->event = NULL;
979 return;
980 }
981
982 fev->fd = of->fd;
983 fev->file = file;
984 fev->cache = cache;
985
986 file->event->handler = ngx_open_file_cache_remove;
987 file->event->data = fev;
988
989 /*
990 * although vnode event may be called while ngx_cycle->poll
991 * destruction, however, cleanup procedures are run before any
992 * memory freeing and events will be canceled.
993 */
994
995 file->event->log = ngx_cycle->log;
996
997 if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT)
998 != NGX_OK)
999 {
1000 ngx_free(file->event->data);
1001 ngx_free(file->event);
1002 file->event = NULL;
1003 return;
1004 }
1005
1006 /*
Igor Sysoevc3584fe2008-06-30 12:12:16 +00001007 * we do not set file->use_event here because there may be a race
1008 * condition: a file may be deleted between opening the file and
1009 * adding event, so we rely upon event notification only after
1010 * one file revalidation on next file access
Igor Sysoev421a3b82007-12-25 10:46:40 +00001011 */
1012
1013 return;
1014}
1015
1016
Igor Sysoevd92bee52007-09-01 12:11:21 +00001017static void
1018ngx_open_file_cleanup(void *data)
1019{
1020 ngx_open_file_cache_cleanup_t *c = data;
1021
1022 c->file->count--;
1023
Igor Sysoevf3b0e492007-12-22 13:19:39 +00001024 ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log);
Igor Sysoevd92bee52007-09-01 12:11:21 +00001025
1026 /* drop one or two expired open files */
1027 ngx_expire_old_cached_files(c->cache, 1, c->log);
1028}
1029
1030
1031static void
1032ngx_close_cached_file(ngx_open_file_cache_t *cache,
Igor Sysoevf3b0e492007-12-22 13:19:39 +00001033 ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log)
Igor Sysoevd92bee52007-09-01 12:11:21 +00001034{
Igor Sysoevf3b0e492007-12-22 13:19:39 +00001035 ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0,
1036 "close cached open file: %s, fd:%d, c:%d, u:%d, %d",
1037 file->name, file->fd, file->count, file->uses, file->close);
Igor Sysoevd92bee52007-09-01 12:11:21 +00001038
1039 if (!file->close) {
1040
1041 file->accessed = ngx_time();
1042
Igor Sysoevff71b942007-12-21 15:33:15 +00001043 ngx_queue_remove(&file->queue);
Igor Sysoevd92bee52007-09-01 12:11:21 +00001044
Igor Sysoevff71b942007-12-21 15:33:15 +00001045 ngx_queue_insert_head(&cache->expire_queue, &file->queue);
Igor Sysoevd92bee52007-09-01 12:11:21 +00001046
Igor Sysoevf3b0e492007-12-22 13:19:39 +00001047 if (file->uses >= min_uses || file->count) {
1048 return;
1049 }
Igor Sysoevd92bee52007-09-01 12:11:21 +00001050 }
1051
Igor Sysoev421a3b82007-12-25 10:46:40 +00001052 ngx_open_file_del_event(file);
Igor Sysoevd92bee52007-09-01 12:11:21 +00001053
1054 if (file->count) {
1055 return;
1056 }
1057
Igor Sysoevf3b0e492007-12-22 13:19:39 +00001058 if (file->fd != NGX_INVALID_FILE) {
1059
1060 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
1061 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
1062 ngx_close_file_n " \"%s\" failed", file->name);
1063 }
1064
1065 file->fd = NGX_INVALID_FILE;
1066 }
1067
1068 if (!file->close) {
1069 return;
Igor Sysoevd92bee52007-09-01 12:11:21 +00001070 }
1071
1072 ngx_free(file->name);
1073 ngx_free(file);
1074}
1075
1076
1077static void
Igor Sysoev421a3b82007-12-25 10:46:40 +00001078ngx_open_file_del_event(ngx_cached_open_file_t *file)
1079{
1080 if (file->event == NULL) {
1081 return;
1082 }
1083
1084 (void) ngx_del_event(file->event, NGX_VNODE_EVENT,
1085 file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT);
1086
1087 ngx_free(file->event->data);
1088 ngx_free(file->event);
1089 file->event = NULL;
1090 file->use_event = 0;
1091}
1092
1093
1094static void
Igor Sysoevd92bee52007-09-01 12:11:21 +00001095ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n,
1096 ngx_log_t *log)
1097{
1098 time_t now;
Igor Sysoevff71b942007-12-21 15:33:15 +00001099 ngx_queue_t *q;
Igor Sysoevd92bee52007-09-01 12:11:21 +00001100 ngx_cached_open_file_t *file;
1101
1102 now = ngx_time();
1103
1104 /*
1105 * n == 1 deletes one or two inactive files
1106 * n == 0 deletes least recently used file by force
1107 * and one or two inactive files
1108 */
1109
1110 while (n < 3) {
1111
Igor Sysoevff71b942007-12-21 15:33:15 +00001112 if (ngx_queue_empty(&cache->expire_queue)) {
Igor Sysoevd92bee52007-09-01 12:11:21 +00001113 return;
1114 }
1115
Igor Sysoevff71b942007-12-21 15:33:15 +00001116 q = ngx_queue_last(&cache->expire_queue);
1117
1118 file = ngx_queue_data(q, ngx_cached_open_file_t, queue);
1119
Igor Sysoevd92bee52007-09-01 12:11:21 +00001120 if (n++ != 0 && now - file->accessed <= cache->inactive) {
1121 return;
1122 }
1123
Igor Sysoevff71b942007-12-21 15:33:15 +00001124 ngx_queue_remove(q);
Igor Sysoevd92bee52007-09-01 12:11:21 +00001125
1126 ngx_rbtree_delete(&cache->rbtree, &file->node);
1127
1128 cache->current--;
1129
1130 ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,
1131 "expire cached open file: %s", file->name);
1132
1133 if (!file->err && !file->is_dir) {
1134 file->close = 1;
Igor Sysoevf3b0e492007-12-22 13:19:39 +00001135 ngx_close_cached_file(cache, file, 0, log);
Igor Sysoevd92bee52007-09-01 12:11:21 +00001136
1137 } else {
1138 ngx_free(file->name);
1139 ngx_free(file);
1140 }
1141 }
1142}
1143
1144
1145static void
1146ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
1147 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
1148{
1149 ngx_rbtree_node_t **p;
1150 ngx_cached_open_file_t *file, *file_temp;
1151
1152 for ( ;; ) {
1153
1154 if (node->key < temp->key) {
1155
1156 p = &temp->left;
1157
1158 } else if (node->key > temp->key) {
1159
1160 p = &temp->right;
1161
1162 } else { /* node->key == temp->key */
1163
1164 file = (ngx_cached_open_file_t *) node;
1165 file_temp = (ngx_cached_open_file_t *) temp;
1166
1167 p = (ngx_strcmp(file->name, file_temp->name) < 0)
1168 ? &temp->left : &temp->right;
1169 }
1170
1171 if (*p == sentinel) {
1172 break;
1173 }
1174
1175 temp = *p;
1176 }
1177
1178 *p = node;
1179 node->parent = temp;
1180 node->left = sentinel;
1181 node->right = sentinel;
1182 ngx_rbt_red(node);
1183}
1184
1185
Igor Sysoev421a3b82007-12-25 10:46:40 +00001186static ngx_cached_open_file_t *
1187ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
1188 uint32_t hash)
1189{
1190 ngx_int_t rc;
1191 ngx_rbtree_node_t *node, *sentinel;
1192 ngx_cached_open_file_t *file;
1193
1194 node = cache->rbtree.root;
1195 sentinel = cache->rbtree.sentinel;
1196
1197 while (node != sentinel) {
1198
1199 if (hash < node->key) {
1200 node = node->left;
1201 continue;
1202 }
1203
1204 if (hash > node->key) {
1205 node = node->right;
1206 continue;
1207 }
1208
1209 /* hash == node->key */
1210
Maxim Dounin7ca6c1f2012-02-27 22:15:39 +00001211 file = (ngx_cached_open_file_t *) node;
Igor Sysoev421a3b82007-12-25 10:46:40 +00001212
Maxim Dounin7ca6c1f2012-02-27 22:15:39 +00001213 rc = ngx_strcmp(name->data, file->name);
Igor Sysoev421a3b82007-12-25 10:46:40 +00001214
Maxim Dounin7ca6c1f2012-02-27 22:15:39 +00001215 if (rc == 0) {
1216 return file;
1217 }
Igor Sysoev421a3b82007-12-25 10:46:40 +00001218
Maxim Dounin7ca6c1f2012-02-27 22:15:39 +00001219 node = (rc < 0) ? node->left : node->right;
Igor Sysoev421a3b82007-12-25 10:46:40 +00001220 }
1221
1222 return NULL;
1223}
1224
1225
Igor Sysoevd92bee52007-09-01 12:11:21 +00001226static void
1227ngx_open_file_cache_remove(ngx_event_t *ev)
1228{
1229 ngx_cached_open_file_t *file;
1230 ngx_open_file_cache_event_t *fev;
1231
1232 fev = ev->data;
1233 file = fev->file;
1234
Igor Sysoevff71b942007-12-21 15:33:15 +00001235 ngx_queue_remove(&file->queue);
Igor Sysoevd92bee52007-09-01 12:11:21 +00001236
1237 ngx_rbtree_delete(&fev->cache->rbtree, &file->node);
1238
1239 fev->cache->current--;
1240
1241 /* NGX_ONESHOT_EVENT was already deleted */
1242 file->event = NULL;
Igor Sysoevbf934762008-06-30 12:11:47 +00001243 file->use_event = 0;
Igor Sysoevd92bee52007-09-01 12:11:21 +00001244
1245 file->close = 1;
1246
Igor Sysoevf3b0e492007-12-22 13:19:39 +00001247 ngx_close_cached_file(fev->cache, file, 0, ev->log);
Igor Sysoevd92bee52007-09-01 12:11:21 +00001248
1249 /* free memory only when fev->cache and fev->file are already not needed */
1250
1251 ngx_free(ev->data);
1252 ngx_free(ev);
1253}