changeset 477:56baf312c1b5 NGINX_0_7_46

nginx 0.7.46 *) Bugfix: the previous release tarball was incorrect.
author Igor Sysoev <http://sysoev.ru>
date Mon, 30 Mar 2009 00:00:00 +0400
parents d46142e61c30
children 3174b44e74fc
files CHANGES CHANGES.ru auto/headers auto/os/features auto/os/linux src/core/nginx.h src/core/ngx_cpuinfo.c src/core/ngx_file.c src/core/ngx_file.h src/core/ngx_slab.c src/core/ngx_slab.h src/event/ngx_event_openssl.c src/http/modules/ngx_http_limit_req_module.c src/http/modules/ngx_http_limit_zone_module.c src/http/modules/perl/nginx.pm src/http/ngx_http_cache.h src/http/ngx_http_core_module.c src/http/ngx_http_file_cache.c src/http/ngx_http_script.c src/http/ngx_http_script.h src/os/unix/ngx_files.c src/os/unix/ngx_files.h src/os/unix/ngx_freebsd_config.h src/os/unix/ngx_linux_config.h src/os/unix/ngx_posix_config.h src/os/unix/ngx_process_cycle.c src/os/unix/ngx_solaris_config.h
diffstat 27 files changed, 724 insertions(+), 254 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,38 @@
 
+Changes with nginx 0.7.46                                        30 Mar 2009
+
+    *) Bugfix: the previous release tarball was incorrect.
+
+
+Changes with nginx 0.7.45                                        30 Mar 2009
+
+    *) Change: now the "proxy_cache" and the "proxy_cache_valid" can be set 
+       on different levels.
+
+    *) Change: the "clean_time" parameter of the "proxy_cache_path" 
+       directive is canceled.
+
+    *) Feature: the "max_size" parameter of the "proxy_cache_path" 
+       directive.
+
+    *) Feature: the ngx_http_fastcgi_module preliminary cache support.
+
+    *) Feature: now on shared memory allocation errors directive and zone 
+       names are logged.
+
+    *) Bugfix: the directive "add_header last-modified ''" did not delete a 
+       "Last-Modified" response header line; the bug had appeared in 0.7.44.
+
+    *) Bugfix: a relative path in the "auth_basic_user_file" directive 
+       given without variables did not work; the bug had appeared in 
+       0.7.44.
+       Thanks to Jerome Loyet.
+
+    *) Bugfix: in an "alias" directive given using variables without 
+       references to captures of regular expressions; the bug had appeared 
+       in 0.7.42.
+
+
 Changes with nginx 0.7.44                                        23 Mar 2009
 
     *) Feature: the ngx_http_proxy_module preliminary cache support.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,36 @@
 
+Изменения в nginx 0.7.46                                          30.03.2009
+
+    *) Исправление: архив предыдущего релиза был неверным.
+
+
+Изменения в nginx 0.7.45                                          30.03.2009
+
+    *) Изменение: теперь директивы proxy_cache и proxy_cache_valid можно 
+       задавать на разных уровнях.
+
+    *) Изменение: параметр clean_time в директиве proxy_cache_path удалён.
+
+    *) Добавление: параметр max_size в директиве proxy_cache_path.
+
+    *) Добавление: предварительная поддержка кэширования в модуле 
+       ngx_http_fastcgi_module.
+
+    *) Добавление: теперь при ошибках выделения в разделяемой памяти в логе 
+       указываются названия директивы и зоны.
+
+    *) Исправление: директива "add_header last-modified ''" не удаляла в 
+       заголовке ответа строку "Last-Modified"; ошибка появилась в 0.7.44.
+
+    *) Исправление: в директиве auth_basic_user_file не работал 
+       относительный путь, заданный строкой без переменных; ошибка 
+       появилась в 0.7.44.
+       Спасибо Jerome Loyet.
+
+    *) Исправление: в директиве alias, заданной переменными без ссылок на 
+       выделения в регулярных выражениях; ошибка появилась в 0.7.42.
+
+
 Изменения в nginx 0.7.44                                          23.03.2009
 
     *) Добавление: предварительная поддержка кэширования в модуле 
--- a/auto/headers
+++ b/auto/headers
@@ -2,8 +2,10 @@
 # Copyright (C) Igor Sysoev
 
 
-ngx_include="unistd.h";    . auto/include
-ngx_include="inttypes.h";  . auto/include
-ngx_include="limits.h";    . auto/include
-ngx_include="sys/filio.h"; . auto/include
-ngx_include="crypt.h";     . auto/include
+ngx_include="unistd.h";      . auto/include
+ngx_include="inttypes.h";    . auto/include
+ngx_include="limits.h";      . auto/include
+ngx_include="sys/filio.h";   . auto/include
+ngx_include="sys/mount.h";   . auto/include
+ngx_include="sys/statvfs.h"; . auto/include
+ngx_include="crypt.h";       . auto/include
--- a/auto/os/features
+++ b/auto/os/features
@@ -205,3 +205,27 @@ ngx_feature_path=
 ngx_feature_libs=
 ngx_feature_test="directio(0, DIRECTIO_ON);"
 . auto/feature
+
+
+ngx_feature="statfs()"
+ngx_feature_name="NGX_HAVE_STATFS"
+ngx_feature_run=no
+ngx_feature_incs="$NGX_INCLUDE_SYS_MOUNT_H
+                  $NGX_INCLUDE_SYS_VFS_H"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct statfs  fs;
+                  statfs(NULL, &fs);"
+. auto/feature
+
+
+ngx_feature="statvfs()"
+ngx_feature_name="NGX_HAVE_STATVFS"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/types.h>
+                  #include <sys/statvfs.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct statvfs  fs;
+                  statvfs(NULL, &fs);"
+. auto/feature
--- a/auto/os/linux
+++ b/auto/os/linux
@@ -124,4 +124,7 @@ ngx_feature_test="long mask = 0;
 . auto/feature
 
 
+ngx_include="sys/vfs.h";     . auto/include
+
+
 CC_AUX_FLAGS=$cc_aux_flags
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.7.45"
+#define nginx_version       007046
+#define NGINX_VERSION      "0.7.46"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_cpuinfo.c
+++ b/src/core/ngx_cpuinfo.c
@@ -72,7 +72,7 @@ void
 ngx_cpuinfo(void)
 {
     u_char    *vendor;
-    uint32_t   vbuf[5], cpu[4];
+    uint32_t   vbuf[5], cpu[4], model;
 
     vbuf[0] = 0;
     vbuf[1] = 0;
@@ -103,8 +103,10 @@ ngx_cpuinfo(void)
         case 6:
             ngx_cacheline_size = 32;
 
-            if ((cpu[0] & 0xf0) >= 0xd0) {
-                /* Intel Core */
+            model = ((cpu[0] & 0xf0000) >> 8) | (cpu[0] & 0xf0);
+
+            if (model >= 0xd0) {
+                /* Intel Core, Core 2, Atom */
                 ngx_cacheline_size = 64;
             }
 
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -264,7 +264,7 @@ ngx_conf_set_path_slot(ngx_conf_t *cf, n
     }
 
     path->len = 0;
-    path->cleaner = (ngx_path_cleaner_pt) cmd->post;
+    path->manager = (ngx_path_manager_pt) cmd->post;
     path->conf_file = cf->conf_file->file.name.data;
     path->line = cf->conf_file->line;
 
@@ -324,7 +324,7 @@ ngx_conf_merge_path_value(ngx_conf_t *cf
                    + init->level[1] + (init->level[1] ? 1 : 0)
                    + init->level[2] + (init->level[2] ? 1 : 0);
 
-    (*path)->cleaner = NULL;
+    (*path)->manager = NULL;
     (*path)->conf_file = NULL;
 
     if (ngx_add_path(cf, path) != NGX_OK) {
--- a/src/core/ngx_file.h
+++ b/src/core/ngx_file.h
@@ -29,7 +29,7 @@ struct ngx_file_s {
 #define NGX_MAX_PATH_LEVEL  3
 
 
-typedef time_t (*ngx_path_cleaner_pt) (void *data);
+typedef time_t (*ngx_path_manager_pt) (void *data);
 
 
 typedef struct {
@@ -37,7 +37,7 @@ typedef struct {
     size_t                     len;
     size_t                     level[3];
 
-    ngx_path_cleaner_pt        cleaner;
+    ngx_path_manager_pt        manager;
     void                      *data;
 
     u_char                    *conf_file;
--- a/src/core/ngx_slab.c
+++ b/src/core/ngx_slab.c
@@ -6,23 +6,6 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 
-/*
-
-                         12
-    2048   2             11
-    1024   4             10
-    512    8             9
-    256   16             8
-
-    128   32   4   32    7
-
-    64    64   8   63    6      1
-    32   128  16  127    5      1
-    16   256  32  254    4      2
-    8    512  64  504    3      8
-
- */
-
 
 #define NGX_SLAB_PAGE_MASK   3
 #define NGX_SLAB_PAGE        0
@@ -80,6 +63,8 @@ static ngx_slab_page_t *ngx_slab_alloc_p
     ngx_uint_t pages);
 static void ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,
     ngx_uint_t pages);
+static void ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level,
+    char *text);
 
 
 static ngx_uint_t  ngx_slab_max_size;
@@ -147,11 +132,8 @@ ngx_slab_init(ngx_slab_pool_t *pool)
         pool->pages->slab = pages;
     }
 
-#if 0
-    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "slab: %p, %p, %ui, %d",
-                  pool, pool->start, pages,
-                  (pool->end - pool->start) / ngx_pagesize - pages);
-#endif
+    pool->log_ctx = &pool->zero;
+    pool->zero = '\0';
 }
 
 
@@ -438,8 +420,7 @@ ngx_slab_free_locked(ngx_slab_pool_t *po
     ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab free: %p", p);
 
     if ((u_char *) p < pool->start || (u_char *) p > pool->end) {
-        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
-                      "ngx_slab_free(): outside of pool");
+        ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_free(): outside of pool");
         goto fail;
     }
 
@@ -587,14 +568,14 @@ ngx_slab_free_locked(ngx_slab_pool_t *po
         }
 
         if (slab == NGX_SLAB_PAGE_FREE) {
-            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
-                          "ngx_slab_free(): page is already free");
+            ngx_slab_error(pool, NGX_LOG_ALERT,
+                           "ngx_slab_free(): page is already free");
             goto fail;
         }
 
         if (slab == NGX_SLAB_PAGE_BUSY) {
-            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
-                          "ngx_slab_free(): pointer to wrong page");
+            ngx_slab_error(pool, NGX_LOG_ALERT,
+                           "ngx_slab_free(): pointer to wrong page");
             goto fail;
         }
 
@@ -620,15 +601,15 @@ done:
 
 wrong_chunk:
 
-    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
-                      "ngx_slab_free(): pointer to wrong chunk");
+    ngx_slab_error(pool, NGX_LOG_ALERT,
+                   "ngx_slab_free(): pointer to wrong chunk");
 
     goto fail;
 
 chunk_already_free:
 
-    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
-                      "ngx_slab_free(): chunk is already free");
+    ngx_slab_error(pool, NGX_LOG_ALERT,
+                   "ngx_slab_free(): chunk is already free");
 
 fail:
 
@@ -679,8 +660,7 @@ ngx_slab_alloc_pages(ngx_slab_pool_t *po
         }
     }
 
-    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, NGX_ENOMEM,
-                  "ngx_slab_alloc(): failed");
+    ngx_slab_error(pool, NGX_LOG_CRIT, "ngx_slab_alloc() failed: no memory");
 
     return NULL;
 }
@@ -711,3 +691,10 @@ ngx_slab_free_pages(ngx_slab_pool_t *poo
 
     pool->free.next = page;
 }
+
+
+static void
+ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level, char *text)
+{
+    ngx_log_error(level, ngx_cycle->log, 0, "%s%s", text, pool->log_ctx);
+}
--- a/src/core/ngx_slab.h
+++ b/src/core/ngx_slab.h
@@ -34,6 +34,9 @@ typedef struct {
     u_char           *end;
 
     ngx_shmtx_t       mutex;
+
+    u_char           *log_ctx;
+    u_char            zero;
 } ngx_slab_pool_t;
 
 
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -1412,6 +1412,7 @@ ngx_ssl_session_cache(ngx_ssl_t *ssl, ng
 static ngx_int_t
 ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)
 {
+    size_t                    len;
     ngx_slab_pool_t          *shpool;
     ngx_ssl_session_cache_t  *cache;
 
@@ -1432,6 +1433,16 @@ ngx_ssl_session_cache_init(ngx_shm_zone_
 
     ngx_queue_init(&cache->expire_queue);
 
+    len = sizeof(" in SSL session shared cache \"\"") + shm_zone->name.len;
+
+    shpool->log_ctx = ngx_slab_alloc(shpool, len);
+    if (shpool->log_ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_sprintf(shpool->log_ctx, " in SSL session shared cache \"%V\"%Z",
+                &shm_zone->name);
+
     shm_zone->data = cache;
 
     return NGX_OK;
--- a/src/http/modules/ngx_http_limit_req_module.c
+++ b/src/http/modules/ngx_http_limit_req_module.c
@@ -224,11 +224,6 @@ ngx_http_limit_req_handler(ngx_http_requ
         node = ngx_slab_alloc_locked(ctx->shpool, n);
         if (node == NULL) {
             ngx_shmtx_unlock(&ctx->shpool->mutex);
-
-            ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
-                          "could not allocate memory in zone \"%V\"",
-                          &lrcf->shm_zone->name);
-
             return NGX_HTTP_SERVICE_UNAVAILABLE;
         }
     }
@@ -457,6 +452,7 @@ ngx_http_limit_req_init_zone(ngx_shm_zon
 {
     ngx_http_limit_req_ctx_t  *octx = data;
 
+    size_t                     len;
     ngx_rbtree_node_t         *sentinel;
     ngx_http_limit_req_ctx_t  *ctx;
 
@@ -500,6 +496,16 @@ ngx_http_limit_req_init_zone(ngx_shm_zon
 
     ngx_queue_init(ctx->queue);
 
+    len = sizeof(" in limit_req zone \"\"") + shm_zone->name.len;
+
+    ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
+    if (ctx->shpool->log_ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z",
+                &shm_zone->name);
+
     return NGX_OK;
 }
 
--- a/src/http/modules/ngx_http_limit_zone_module.c
+++ b/src/http/modules/ngx_http_limit_zone_module.c
@@ -210,11 +210,6 @@ ngx_http_limit_zone_handler(ngx_http_req
     node = ngx_slab_alloc_locked(shpool, n);
     if (node == NULL) {
         ngx_shmtx_unlock(&shpool->mutex);
-
-        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
-                      "could not allocate memory in zone \"%V\"",
-                      &lzcf->shm_zone->name);
-
         return NGX_HTTP_SERVICE_UNAVAILABLE;
     }
 
@@ -321,6 +316,7 @@ ngx_http_limit_zone_init_zone(ngx_shm_zo
 {
     ngx_http_limit_zone_ctx_t  *octx = data;
 
+    size_t                      len;
     ngx_slab_pool_t            *shpool;
     ngx_rbtree_node_t          *sentinel;
     ngx_http_limit_zone_ctx_t  *ctx;
@@ -356,6 +352,15 @@ ngx_http_limit_zone_init_zone(ngx_shm_zo
     ngx_rbtree_init(ctx->rbtree, sentinel,
                     ngx_http_limit_zone_rbtree_insert_value);
 
+    len = sizeof(" in limit_zone \"\"") + shm_zone->name.len;
+
+    shpool->log_ctx = ngx_slab_alloc(shpool, len);
+    if (shpool->log_ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_sprintf(shpool->log_ctx, " in limit_zone \"%V\"%Z", &shm_zone->name);
+
     return NGX_OK;
 }
 
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.7.45';
+our $VERSION = '0.7.46';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_cache.h
+++ b/src/http/ngx_http_cache.h
@@ -48,6 +48,7 @@ typedef struct {
     time_t                           expire;
     time_t                           valid_sec;
     size_t                           body_start;
+    off_t                            length;
 } ngx_http_file_cache_node_t;
 
 
@@ -100,10 +101,16 @@ struct ngx_http_file_cache_s {
 
     ngx_path_t                      *path;
 
+    ngx_atomic_t                    *cold;
+    off_t                           *size;
+
+    off_t                            max_size;
+    size_t                           bsize;
+
     time_t                           inactive;
-    time_t                           created;
-    time_t                           clean_time;
-    time_t                           next_clean_time;
+
+    ngx_msec_t                       last;
+    ngx_uint_t                       files;
 
     ngx_shm_zone_t                  *shm_zone;
 };
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -1695,7 +1695,15 @@ ngx_http_map_uri_to_path(ngx_http_reques
         last = ngx_copy(path->data, clcf->root.data, clcf->root.len);
 
     } else {
-        reserved += alias ? 1 : r->uri.len + 1;
+
+#if (NGX_PCRE)
+        ngx_uint_t  captures;
+
+        captures = alias && clcf->captures;
+        reserved += captures ? 1 : r->uri.len - alias + 1;
+#else
+        reserved += r->uri.len - alias + 1;
+#endif
 
         if (ngx_http_script_run(r, path, clcf->root_lengths->elts, reserved,
                                 clcf->root_values->elts)
@@ -1711,10 +1719,12 @@ ngx_http_map_uri_to_path(ngx_http_reques
         *root_length = path->len - reserved;
         last = path->data + *root_length;
 
-        if (alias) {
+#if (NGX_PCRE)
+        if (captures) {
             *last = '\0';
             return last;
         }
+#endif
     }
 
     last = ngx_cpystrn(last, r->uri.data + alias, r->uri.len - alias + 1);
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -10,18 +10,28 @@
 #include <ngx_md5.h>
 
 
-static ngx_int_t ngx_http_file_cache_exists(ngx_http_request_t *r,
-    ngx_http_file_cache_t *cache);
+static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache,
+    ngx_http_cache_t *c);
 static ngx_http_file_cache_node_t *
     ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key);
 static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
 static void ngx_http_file_cache_cleanup(void *data);
-static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache,
-    ngx_uint_t force);
-static ngx_int_t ngx_http_file_cache_clean_noop(ngx_tree_ctx_t *ctx,
+static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache);
+static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache);
+static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache,
+    ngx_queue_t *q, u_char *name);
+static ngx_int_t
+    ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache);
+static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx,
     ngx_str_t *path);
-static ngx_int_t ngx_http_file_cache_clean_file(ngx_tree_ctx_t *ctx,
+static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx,
+    ngx_str_t *path);
+static ngx_int_t ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx,
+    ngx_str_t *path);
+static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache,
+    ngx_http_cache_t *c);
+static ngx_int_t ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx,
     ngx_str_t *path);
 
 
@@ -33,6 +43,7 @@ ngx_http_file_cache_init(ngx_shm_zone_t 
 {
     ngx_http_file_cache_t  *ocache = data;
 
+    size_t                  len;
     ngx_rbtree_node_t      *sentinel;
     ngx_http_file_cache_t  *cache;
 
@@ -52,7 +63,11 @@ ngx_http_file_cache_init(ngx_shm_zone_t 
         cache->rbtree = ocache->rbtree;
         cache->queue = ocache->queue;
         cache->shpool = ocache->shpool;
-        cache->created = ocache->created;
+        cache->cold = ocache->cold;
+        cache->size = ocache->size;
+        cache->bsize = ocache->bsize;
+
+        cache->max_size /= cache->bsize;
 
         return NGX_OK;
     }
@@ -79,7 +94,33 @@ ngx_http_file_cache_init(ngx_shm_zone_t 
 
     ngx_queue_init(cache->queue);
 
-    cache->created = ngx_time();
+    cache->cold = ngx_slab_alloc(cache->shpool, sizeof(ngx_atomic_t));
+    if (cache->cold == NULL) {
+        return NGX_ERROR;
+    }
+
+    *cache->cold = 1;
+
+    cache->size = ngx_slab_alloc(cache->shpool, sizeof(off_t));
+    if (cache->size == NULL) {
+        return NGX_ERROR;
+    }
+
+    *cache->size = 0;
+
+    cache->bsize = ngx_fs_bsize(cache->path->name.data);
+
+    cache->max_size /= cache->bsize;
+
+    len = sizeof(" in cache keys zone \"\"") + shm_zone->name.len;
+
+    cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len);
+    if (cache->shpool->log_ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_sprintf(cache->shpool->log_ctx, " in cache keys zone \"%V\"%Z",
+                &shm_zone->name);
 
     return NGX_OK;
 }
@@ -144,7 +185,7 @@ ngx_http_file_cache_open(ngx_http_reques
         return NGX_ERROR;
     }
 
-    rc = ngx_http_file_cache_exists(r, cache);
+    rc = ngx_http_file_cache_exists(cache, c);
 
     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http file cache exists: %i u:%ui e:%d",
@@ -161,9 +202,7 @@ ngx_http_file_cache_open(ngx_http_reques
         return rc;
     }
 
-    now = ngx_time();
-
-    cold = (now - cache->created < cache->inactive) ? 1 : 0;
+    cold = *cache->cold;
 
     if (rc == NGX_OK) {
 
@@ -293,13 +332,19 @@ ngx_http_file_cache_open(ngx_http_reques
 
         ngx_shmtx_lock(&cache->shpool->mutex);
 
-        c->node->uses = c->min_uses;
-        c->node->body_start = c->body_start;
-        c->node->exists = 1;
+        if (!c->node->exists) {
+            c->node->uses = 1;
+            c->node->body_start = c->body_start;
+            c->node->exists = 1;
+
+            *cache->size += (c->length + cache->bsize - 1) / cache->bsize;
+        }
 
         ngx_shmtx_unlock(&cache->shpool->mutex);
     }
 
+    now = ngx_time();
+
     if (c->valid_sec < now) {
 
         c->uses = c->min_uses;
@@ -317,14 +362,14 @@ ngx_http_file_cache_open(ngx_http_reques
 
 
 static ngx_int_t
-ngx_http_file_cache_exists(ngx_http_request_t *r, ngx_http_file_cache_t *cache)
+ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
 {
     ngx_int_t                    rc;
     ngx_http_file_cache_node_t  *fcn;
 
     ngx_shmtx_lock(&cache->shpool->mutex);
 
-    fcn = ngx_http_file_cache_lookup(cache, r->cache->key);
+    fcn = ngx_http_file_cache_lookup(cache, c->key);
 
     if (fcn) {
         ngx_queue_remove(&fcn->queue);
@@ -345,18 +390,18 @@ ngx_http_file_cache_exists(ngx_http_requ
 
         if (fcn->exists) {
 
-            r->cache->exists = fcn->exists;
-            r->cache->body_start = fcn->body_start;
+            c->exists = fcn->exists;
+            c->body_start = fcn->body_start;
 
             rc = NGX_OK;
 
             goto done;
         }
 
-        if (fcn->uses >= r->cache->min_uses) {
+        if (fcn->uses >= c->min_uses) {
 
-            r->cache->exists = fcn->exists;
-            r->cache->body_start = fcn->body_start;
+            c->exists = fcn->exists;
+            c->body_start = fcn->body_start;
 
             rc = NGX_OK;
 
@@ -372,7 +417,7 @@ ngx_http_file_cache_exists(ngx_http_requ
     if (fcn == NULL) {
         ngx_shmtx_unlock(&cache->shpool->mutex);
 
-        (void) ngx_http_file_cache_expire(cache, 1);
+        ngx_http_file_cache_forced_expire(cache);
 
         ngx_shmtx_lock(&cache->shpool->mutex);
 
@@ -384,10 +429,9 @@ ngx_http_file_cache_exists(ngx_http_requ
         }
     }
 
-    ngx_memcpy((u_char *) &fcn->node.key, r->cache->key,
-               sizeof(ngx_rbtree_key_t));
+    ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
 
-    ngx_memcpy(fcn->key, &r->cache->key[sizeof(ngx_rbtree_key_t)],
+    ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
                NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
 
     ngx_rbtree_insert(cache->rbtree, &fcn->node);
@@ -411,10 +455,10 @@ done:
 
     ngx_queue_insert_head(cache->queue, &fcn->queue);
 
-    r->cache->uniq = fcn->uniq;
-    r->cache->uses = fcn->uses;
-    r->cache->error = fcn->error;
-    r->cache->node = fcn;
+    c->uniq = fcn->uniq;
+    c->uses = fcn->uses;
+    c->error = fcn->error;
+    c->node = fcn;
 
 failed:
 
@@ -556,6 +600,7 @@ ngx_http_file_cache_set_header(ngx_http_
 void
 ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf)
 {
+    off_t                   size;
     ngx_int_t               rc;
     ngx_file_uniq_t         uniq;
     ngx_file_info_t         fi;
@@ -605,12 +650,20 @@ ngx_http_file_cache_update(ngx_http_requ
         }
     }
 
+    size = (c->length + cache->bsize - 1) / cache->bsize;
+
     ngx_shmtx_lock(&cache->shpool->mutex);
 
     c->node->count--;
     c->node->uniq = uniq;
     c->node->body_start = c->body_start;
 
+    size = size - (c->node->length + cache->bsize - 1) / cache->bsize;
+
+    c->node->length = c->length;
+
+    *cache->size += size;
+
     if (rc == NGX_OK) {
         c->node->exists = 1;
     }
@@ -746,12 +799,84 @@ ngx_http_file_cache_cleanup(void *data)
 
 
 static time_t
-ngx_http_file_cache_expire(ngx_http_file_cache_t *cache, ngx_uint_t forced)
+ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache)
+{
+    u_char                      *name;
+    size_t                       len;
+    time_t                       wait;
+    ngx_uint_t                   tries;
+    ngx_path_t                  *path;
+    ngx_queue_t                 *q;
+    ngx_http_file_cache_node_t  *fcn;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                   "http file cache forced expire");
+
+    path = cache->path;
+    len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
+
+    name = ngx_alloc(len + 1, ngx_cycle->log);
+    if (name == NULL) {
+        return 60;
+    }
+
+    ngx_memcpy(name, path->name.data, path->name.len);
+
+    wait = 60;
+    tries = 0;
+
+    ngx_shmtx_lock(&cache->shpool->mutex);
+
+    for (q = ngx_queue_last(cache->queue);
+         q != ngx_queue_sentinel(cache->queue);
+         q = ngx_queue_prev(q))
+    {
+        fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
+
+        ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                  "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd",
+                  fcn->count, fcn->exists,
+                  fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);
+
+        if (fcn->count) {
+
+            if (tries++ < 20) {
+                continue;
+            }
+
+            wait = 1;
+
+            break;
+        }
+
+        if (!fcn->exists) {
+
+            ngx_queue_remove(q);
+            ngx_rbtree_delete(cache->rbtree, &fcn->node);
+            ngx_slab_free_locked(cache->shpool, fcn);
+
+            break;
+        }
+
+        ngx_http_file_cache_delete(cache, q, name);
+
+        break;
+    }
+
+    ngx_shmtx_unlock(&cache->shpool->mutex);
+
+    ngx_free(name);
+
+    return wait;
+}
+
+
+static time_t
+ngx_http_file_cache_expire(ngx_http_file_cache_t *cache)
 {
     u_char                      *name, *p;
     size_t                       len;
     time_t                       now, wait;
-    ngx_uint_t                   tries;
     ngx_path_t                  *path;
     ngx_queue_t                 *q;
     ngx_http_file_cache_node_t  *fcn;
@@ -763,16 +888,21 @@ ngx_http_file_cache_expire(ngx_http_file
     path = cache->path;
     len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
 
+    name = ngx_alloc(len + 1, ngx_cycle->log);
+    if (name == NULL) {
+        return 60;
+    }
+
+    ngx_memcpy(name, path->name.data, path->name.len);
+
     now = ngx_time();
 
     ngx_shmtx_lock(&cache->shpool->mutex);
 
-    tries = 0;
-
     for ( ;; ) {
 
         if (ngx_queue_empty(cache->queue)) {
-            wait = cache->inactive;
+            wait = 60;
             break;
         }
 
@@ -780,12 +910,11 @@ ngx_http_file_cache_expire(ngx_http_file
 
         fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
 
-        if (!forced) {
-            wait = fcn->expire - now;
+        wait = fcn->expire - now;
 
-            if (wait > 0) {
-                break;
-            }
+        if (wait > 0) {
+            wait = wait > 60 ? 60 : wait;
+            break;
         }
 
         ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
@@ -795,198 +924,360 @@ ngx_http_file_cache_expire(ngx_http_file
 
         if (fcn->count) {
 
-            if (!forced) {
+            p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
+                             sizeof(ngx_rbtree_key_t));
 
-                p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
-                                 sizeof(ngx_rbtree_key_t));
-                (void) ngx_hex_dump(p, fcn->key,
-                            NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
+            len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
+            (void) ngx_hex_dump(p, fcn->key, len);
 
-                /*
-                 * abnormally exited workers may leave locked cache entries,
-                 * and although it may be safe to remove them completely,
-                 * we prefer to remove them from inactive queue and rbtree
-                 * only, and to allow other leaks
-                 */
+            /*
+             * abnormally exited workers may leave locked cache entries,
+             * and although it may be safe to remove them completely,
+             * we prefer to remove them from inactive queue and rbtree
+             * only, and to allow other leaks
+             */
 
-                ngx_queue_remove(q);
+            ngx_queue_remove(q);
+            ngx_rbtree_delete(cache->rbtree, &fcn->node);
 
-                ngx_rbtree_delete(cache->rbtree, &fcn->node);
-
-                ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
                        "ignore long locked inactive cache entry %*s, count:%d",
                        2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);
-            }
 
-            if (tries++ < 10) {
-                continue;
-            }
-
-            wait = 1;
-            break;
+            continue;
         }
 
-        forced = 0;
-
         if (!fcn->exists) {
 
             ngx_queue_remove(q);
-
             ngx_rbtree_delete(cache->rbtree, &fcn->node);
-
             ngx_slab_free_locked(cache->shpool, fcn);
 
             continue;
         }
 
-        name = ngx_alloc(len + 1, ngx_cycle->log);
-        if (name == NULL) {
-            wait = 60;
-            break;
-        }
-
-        ngx_memcpy(name, path->name.data, path->name.len);
-
-        p = name + path->name.len + 1 + path->len;
-        p = ngx_hex_dump(p, (u_char *) &fcn->node.key,
-                         sizeof(ngx_rbtree_key_t));
-        p = ngx_hex_dump(p, fcn->key,
-                         NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
-        *p = '\0';
-
-        ngx_queue_remove(q);
-
-        ngx_rbtree_delete(cache->rbtree, &fcn->node);
-
-        ngx_slab_free_locked(cache->shpool, fcn);
-
-        ngx_shmtx_unlock(&cache->shpool->mutex);
-
-        ngx_create_hashed_filename(path, name, len);
-
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
-                       "http file cache expire: \"%s\"", name);
-
-        if (ngx_delete_file(name) == NGX_FILE_ERROR) {
-            ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,
-                          ngx_delete_file_n " \"%s\" failed", name);
-        }
-
-        ngx_free(name);
-
-        ngx_shmtx_lock(&cache->shpool->mutex);
+        ngx_http_file_cache_delete(cache, q, name);
     }
 
     ngx_shmtx_unlock(&cache->shpool->mutex);
 
+    ngx_free(name);
+
     return wait;
 }
 
 
-time_t
-ngx_http_file_cache_cleaner(void *data)
+static void
+ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q,
+    u_char *name)
+{
+    u_char                      *p;
+    size_t                       len;
+    ngx_path_t                  *path;
+    ngx_http_file_cache_node_t  *fcn;
+
+    fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
+
+    *cache->size -= (fcn->length + cache->bsize - 1) / cache->bsize;
+
+    path = cache->path;
+
+    p = name + path->name.len + 1 + path->len;
+
+    p = ngx_hex_dump(p, (u_char *) &fcn->node.key, sizeof(ngx_rbtree_key_t));
+
+    len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
+    p = ngx_hex_dump(p, fcn->key, len);
+    *p = '\0';
+
+    ngx_queue_remove(q);
+
+    ngx_rbtree_delete(cache->rbtree, &fcn->node);
+
+    ngx_slab_free_locked(cache->shpool, fcn);
+
+    ngx_shmtx_unlock(&cache->shpool->mutex);
+
+    len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
+
+    ngx_create_hashed_filename(path, name, len);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                   "http file cache expire: \"%s\"", name);
+
+    if (ngx_delete_file(name) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,
+                      ngx_delete_file_n " \"%s\" failed", name);
+    }
+
+    ngx_shmtx_lock(&cache->shpool->mutex);
+}
+
+
+static time_t
+ngx_http_file_cache_manager(void *data)
 {
     ngx_http_file_cache_t  *cache = data;
 
-    time_t          now, next;
+    off_t           size;
+    time_t          next;
     ngx_tree_ctx_t  tree;
 
-    now = ngx_time();
+    if (*cache->cold) {
 
-    if (now >= cache->next_clean_time
-        && now >= cache->created + cache->inactive)
-    {
-        ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
-                      "clean unused cache files");
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                       "http file cache manager update");
 
         tree.init_handler = NULL;
-        tree.file_handler = ngx_http_file_cache_clean_file;
-        tree.pre_tree_handler = ngx_http_file_cache_clean_noop;
-        tree.post_tree_handler = ngx_http_file_cache_clean_noop;
-        tree.spec_handler = ngx_http_file_cache_clean_file;
+        tree.file_handler = ngx_http_file_cache_manage_file;
+        tree.pre_tree_handler = ngx_http_file_cache_noop;
+        tree.post_tree_handler = ngx_http_file_cache_noop;
+        tree.spec_handler = ngx_http_file_cache_delete_file;
         tree.data = cache;
         tree.alloc = 0;
         tree.log = ngx_cycle->log;
 
-        (void) ngx_walk_tree(&tree, &cache->path->name);
-
-        ngx_time_update(0, 0);
+        cache->last = ngx_current_msec;
+        cache->files = 0;
 
-        next = ngx_next_time(cache->clean_time);
-
-        if (next == -1) {
-            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
-                          ngx_next_time_n " failed");
+        if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {
             return 60;
         }
 
-        cache->next_clean_time = next;
+        *cache->cold = 0;
+
+        ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
+                      "http file cache: %V %.3fM, bsize: %uz",
+                      &cache->path->name,
+                      ((double) *cache->size * cache->bsize) / (1024 * 1024),
+                      cache->bsize);
     }
 
-    next = ngx_http_file_cache_expire(cache, 0);
+    next = ngx_http_file_cache_expire(cache);
+
+    cache->last = ngx_current_msec;
+    cache->files = 0;
+
+    for ( ;; ) {
+        ngx_shmtx_lock(&cache->shpool->mutex);
+
+        size = *cache->size;
 
-    now = ngx_time();
+        ngx_shmtx_unlock(&cache->shpool->mutex);
+
+        if (size < cache->max_size) {
+            return next;
+        }
 
-    return (now + next < cache->next_clean_time) ? next:
-                                                   cache->next_clean_time - now;
+        next = ngx_http_file_cache_forced_expire(cache);
+
+        if (ngx_http_file_cache_manager_sleep(cache) != NGX_OK) {
+            return next;
+        }
+    }
 }
 
 
 static ngx_int_t
-ngx_http_file_cache_clean_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache)
+{
+    ngx_msec_t  elapsed;
+
+    if (cache->files++ > 100) {
+
+        ngx_time_update(0, 0);
+
+        elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                       "http file cache manager time: %M", elapsed);
+
+        if (elapsed > 200) {
+
+            /*
+             * if processing 100 files takes more than 200ms,
+             * it seems that many operations require disk i/o,
+             * therefore sleep 200ms
+             */
+
+            ngx_msleep(200);
+
+            ngx_time_update(0, 0);
+        }
+
+        cache->last = ngx_current_msec;
+        cache->files = 0;
+    }
+
+    return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
 {
     return NGX_OK;
 }
 
 
 static ngx_int_t
-ngx_http_file_cache_clean_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
 {
-    u_char                      *p, key[2 * NGX_HTTP_CACHE_KEY_LEN];
-    ngx_int_t                    n;
-    ngx_uint_t                   i;
-    ngx_http_file_cache_t       *cache;
-    ngx_http_file_cache_node_t  *node;
+    ngx_http_file_cache_t  *cache;
+
+    cache = ctx->data;
+
+    if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) {
+        (void) ngx_http_file_cache_delete_file(ctx, path);
+    }
+
+    return ngx_http_file_cache_manager_sleep(cache);
+}
+
 
-    if (path->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {
-        goto clean;
+static ngx_int_t
+ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name)
+{
+    u_char                        *p;
+    ngx_fd_t                       fd;
+    ngx_int_t                      n;
+    ngx_uint_t                     i;
+    ngx_file_info_t                fi;
+    ngx_http_cache_t               c;
+    ngx_http_file_cache_t         *cache;
+    ngx_http_file_cache_header_t   h;
+
+    if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {
+        return NGX_ERROR;
     }
 
-    p = &path->data[path->len - 2 * NGX_HTTP_CACHE_KEY_LEN];
+    ngx_memzero(&c, sizeof(ngx_http_cache_t));
+
+    fd = ngx_open_file(name->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+
+    if (fd == NGX_INVALID_FILE) {
+        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
+                      ngx_open_file_n " \"%s\" failed", name->data);
+        return NGX_ERROR;
+    }
+
+    c.file.fd = fd;
+    c.file.name = *name;
+    c.file.log = ctx->log;
+
+    n = ngx_read_file(&c.file, (u_char *) &h,
+                      sizeof(ngx_http_file_cache_header_t), 0);
+    if (n == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    if ((size_t) n < sizeof(ngx_http_file_cache_header_t)) {
+        ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,
+                      "cache file \"%s\" is too small", name->data);
+        return NGX_ERROR;
+    }
+
+    if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
+                      ngx_fd_info_n " \"%s\" failed", name->data);
+
+    } else {
+        c.uniq = ngx_file_uniq(&fi);
+        c.valid_sec = h.valid_sec;
+        c.valid_msec = h.valid_msec;
+        c.body_start = h.body_start;
+        c.length = ngx_file_size(&fi);
+    }
+
+    if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
+                      ngx_close_file_n " \"%s\" failed", name->data);
+    }
+
+    if (c.body_start == 0) {
+        return NGX_ERROR;
+    }
+
+    p = &name->data[name->len - 2 * NGX_HTTP_CACHE_KEY_LEN];
 
     for (i = 0; i < NGX_HTTP_CACHE_KEY_LEN; i++) {
         n = ngx_hextoi(p, 2);
 
         if (n == NGX_ERROR) {
-            goto clean;
+            return NGX_ERROR;
         }
 
         p += 2;
 
-        key[i] = (u_char) n;
+        c.key[i] = (u_char) n;
     }
 
     cache = ctx->data;
 
+    return ngx_http_file_cache_add(cache, &c);
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
+{
+    ngx_http_file_cache_node_t  *fcn;
+
     ngx_shmtx_lock(&cache->shpool->mutex);
 
-    node = ngx_http_file_cache_lookup(cache, key);
+    fcn = ngx_http_file_cache_lookup(cache, c->key);
+
+    if (fcn == NULL) {
+
+        fcn = ngx_slab_alloc_locked(cache->shpool,
+                                    sizeof(ngx_http_file_cache_node_t));
+        if (fcn == NULL) {
+            ngx_shmtx_unlock(&cache->shpool->mutex);
+            return NGX_ERROR;
+        }
+
+        ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
+
+        ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
+                   NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
+
+        ngx_rbtree_insert(cache->rbtree, &fcn->node);
+
+        fcn->uses = 1;
+        fcn->count = 0;
+        fcn->valid_msec = c->valid_msec;
+        fcn->error = 0;
+        fcn->exists = 1;
+        fcn->uniq = c->uniq;
+        fcn->valid_sec = c->valid_sec;
+        fcn->body_start = c->body_start;
+        fcn->length = c->length;
+
+        *cache->size += (c->length + cache->bsize - 1) / cache->bsize;
+
+    } else {
+        ngx_queue_remove(&fcn->queue);
+    }
+
+    fcn->expire = ngx_time() + cache->inactive;
+
+    ngx_queue_insert_head(cache->queue, &fcn->queue);
 
     ngx_shmtx_unlock(&cache->shpool->mutex);
 
-    if (node) {
-        return NGX_OK;
-    }
+    return NGX_OK;
+}
+
 
-clean:
-
+static ngx_int_t
+ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
-                   "http file cache clean: \"%s\"", path->data);
+                   "http file cache delete: \"%s\"", path->data);
 
     if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
         ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
                       ngx_delete_file_n " \"%s\" failed", path->data);
-        return NGX_ERROR;
     }
 
     return NGX_OK;
@@ -1018,8 +1309,9 @@ ngx_http_file_cache_valid(ngx_array_t *c
 char *
 ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
+    off_t                   max_size;
     u_char                 *last, *p;
-    time_t                  inactive, clean_time, next;
+    time_t                  inactive;
     ssize_t                 size;
     ngx_str_t               s, name, *value;
     ngx_uint_t              i, n;
@@ -1036,10 +1328,10 @@ ngx_http_file_cache_set_slot(ngx_conf_t 
     }
 
     inactive = 600;
-    clean_time = 5 * 60 * 60;
 
     name.len = 0;
     size = 0;
+    max_size = NGX_MAX_OFF_T_VALUE;
 
     value = cf->args->elts;
 
@@ -1142,15 +1434,15 @@ ngx_http_file_cache_set_slot(ngx_conf_t 
             continue;
         }
 
-        if (ngx_strncmp(value[i].data, "clean_time=", 11) == 0) {
+        if (ngx_strncmp(value[i].data, "max_size=", 9) == 0) {
 
-            s.len = value[i].len - 11;
-            s.data = value[i].data + 11;
+            s.len = value[i].len - 9;
+            s.data = value[i].data + 9;
 
-            clean_time = ngx_parse_time(&s, 1);
-            if (clean_time < 0 || clean_time > 24 * 60 * 60) {
+            max_size = ngx_parse_offset(&s);
+            if (max_size < 0) {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                                 "invalid clean_time value \"%V\"", &value[i]);
+                                   "invalid max_size value \"%V\"", &value[i]);
                 return NGX_CONF_ERROR;
             }
 
@@ -1169,7 +1461,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t 
         return NGX_CONF_ERROR;
     }
 
-    cache->path->cleaner = ngx_http_file_cache_cleaner;
+    cache->path->manager = ngx_http_file_cache_manager;
     cache->path->data = cache;
 
     if (ngx_add_path(cf, &cache->path) != NGX_OK) {
@@ -1187,20 +1479,12 @@ ngx_http_file_cache_set_slot(ngx_conf_t 
         return NGX_CONF_ERROR;
     }
 
-    next = ngx_next_time(clean_time);
-
-    if (next == -1) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
-                           ngx_next_time_n " failed");
-        return NGX_CONF_ERROR;
-    }
 
     cache->shm_zone->init = ngx_http_file_cache_init;
     cache->shm_zone->data = cache;
 
     cache->inactive = inactive;
-    cache->clean_time = clean_time;
-    cache->next_clean_time = next;
+    cache->max_size = max_size;
 
     return NGX_CONF_OK;
 }
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -32,7 +32,7 @@ static uintptr_t ngx_http_script_exit_co
 
 
 void
-ngx_http_scrip_flush_complex_value(ngx_http_request_t *r,
+ngx_http_script_flush_complex_value(ngx_http_request_t *r,
     ngx_http_complex_value_t *val)
 {
     ngx_uint_t *index;
@@ -67,7 +67,7 @@ ngx_http_complex_value(ngx_http_request_
         return NGX_OK;
     }
 
-    ngx_http_scrip_flush_complex_value(r, val);
+    ngx_http_script_flush_complex_value(r, val);
 
     ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
 
@@ -118,11 +118,6 @@ ngx_http_compile_complex_value(ngx_http_
         return NGX_ERROR;
     }
 
-    ccv->complex_value->value = *v;
-    ccv->complex_value->flushes = NULL;
-    ccv->complex_value->lengths = NULL;
-    ccv->complex_value->values = NULL;
-
     nv = 0;
     nc = 0;
 
@@ -147,6 +142,11 @@ ngx_http_compile_complex_value(ngx_http_
         ccv->root_prefix = 0;
     }
 
+    ccv->complex_value->value = *v;
+    ccv->complex_value->flushes = NULL;
+    ccv->complex_value->lengths = NULL;
+    ccv->complex_value->values = NULL;
+
     if (nv == 0 && nc == 0) {
         return NGX_OK;
     }
--- a/src/http/ngx_http_script.h
+++ b/src/http/ngx_http_script.h
@@ -203,7 +203,7 @@ typedef struct {
 } ngx_http_script_value_code_t;
 
 
-void ngx_http_scrip_flush_complex_value(ngx_http_request_t *r,
+void ngx_http_script_flush_complex_value(ngx_http_request_t *r,
     ngx_http_complex_value_t *val);
 ngx_int_t ngx_http_complex_value(ngx_http_request_t *r,
     ngx_http_complex_value_t *val, ngx_str_t *value);
--- a/src/os/unix/ngx_files.c
+++ b/src/os/unix/ngx_files.c
@@ -416,3 +416,50 @@ ngx_directio_off(ngx_fd_t fd)
 }
 
 #endif
+
+
+#if (NGX_HAVE_STATFS)
+
+size_t
+ngx_fs_bsize(u_char *name)
+{
+    struct statfs  fs;
+
+    if (statfs((char *) name, &fs) == -1) {
+        return 512;
+    }
+
+    if ((fs.f_bsize % 512) != 0) {
+        return 512;
+    }
+
+    return (size_t) fs.f_bsize;
+}
+
+#elif (NGX_HAVE_STATVFS)
+
+size_t
+ngx_fs_bsize(u_char *name)
+{
+    struct statvfs  fs;
+
+    if (statvfs((char *) name, &fs) == -1) {
+        return 512;
+    }
+
+    if ((fs.f_frsize % 512) != 0) {
+        return 512;
+    }
+
+    return (size_t) fs.f_frsize;
+}
+
+#else
+
+size_t
+ngx_fs_bsize(u_char *name)
+{
+    return 512;
+}
+
+#endif
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -274,4 +274,7 @@ ngx_int_t ngx_directio_off(ngx_fd_t fd);
 #endif
 
 
+size_t ngx_fs_bsize(u_char *name);
+
+
 #endif /* _NGX_FILES_H_INCLUDED_ */
--- a/src/os/unix/ngx_freebsd_config.h
+++ b/src/os/unix/ngx_freebsd_config.h
@@ -22,6 +22,7 @@
 #include <grp.h>
 #include <dirent.h>
 #include <glob.h>
+#include <sys/mount.h>          /* statfs() */
 
 #include <sys/filio.h>          /* FIONBIO */
 #include <sys/uio.h>
--- a/src/os/unix/ngx_linux_config.h
+++ b/src/os/unix/ngx_linux_config.h
@@ -28,6 +28,7 @@
 #include <grp.h>
 #include <dirent.h>
 #include <glob.h>
+#include <sys/vfs.h>            /* statfs() */
 
 #include <sys/uio.h>
 #include <sys/stat.h>
--- a/src/os/unix/ngx_posix_config.h
+++ b/src/os/unix/ngx_posix_config.h
@@ -44,6 +44,12 @@
 #include <grp.h>
 #include <dirent.h>
 #include <glob.h>
+#if (NGX_HAVE_SYS_MOUNT_H)
+#include <sys/mount.h>          /* statfs() */
+#endif
+#if (NGX_HAVE_SYS_STATVFS_H)
+#include <sys/statvfs.h>        /* statvfs() */
+#endif
 
 #if (NGX_HAVE_SYS_FILIO_H)
 #include <sys/filio.h>          /* FIONBIO */
--- a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -12,7 +12,7 @@
 
 static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n,
     ngx_int_t type);
-static void ngx_start_cleaner_process(ngx_cycle_t *cycle, ngx_int_t type);
+static void ngx_start_cache_manager_process(ngx_cycle_t *cycle, ngx_int_t type);
 static void ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo);
 static ngx_uint_t ngx_reap_children(ngx_cycle_t *cycle);
 static void ngx_master_process_exit(ngx_cycle_t *cycle);
@@ -24,8 +24,8 @@ static void ngx_channel_handler(ngx_even
 static void ngx_wakeup_worker_threads(ngx_cycle_t *cycle);
 static ngx_thread_value_t ngx_worker_thread_cycle(void *data);
 #endif
-static void ngx_cleaner_process_cycle(ngx_cycle_t *cycle, void *data);
-static void ngx_cleaner_process_handler(ngx_event_t *ev);
+static void ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data);
+static void ngx_cache_manager_process_handler(ngx_event_t *ev);
 
 
 ngx_uint_t    ngx_process;
@@ -122,7 +122,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy
 
     ngx_start_worker_processes(cycle, ccf->worker_processes,
                                NGX_PROCESS_RESPAWN);
-    ngx_start_cleaner_process(cycle, NGX_PROCESS_RESPAWN);
+    ngx_start_cache_manager_process(cycle, NGX_PROCESS_RESPAWN);
 
     ngx_new_binary = 0;
     delay = 0;
@@ -203,7 +203,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy
             if (ngx_new_binary) {
                 ngx_start_worker_processes(cycle, ccf->worker_processes,
                                            NGX_PROCESS_RESPAWN);
-                ngx_start_cleaner_process(cycle, NGX_PROCESS_RESPAWN);
+                ngx_start_cache_manager_process(cycle, NGX_PROCESS_RESPAWN);
                 ngx_noaccepting = 0;
 
                 continue;
@@ -222,7 +222,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy
                                                    ngx_core_module);
             ngx_start_worker_processes(cycle, ccf->worker_processes,
                                        NGX_PROCESS_JUST_RESPAWN);
-            ngx_start_cleaner_process(cycle, NGX_PROCESS_JUST_RESPAWN);
+            ngx_start_cache_manager_process(cycle, NGX_PROCESS_JUST_RESPAWN);
             live = 1;
             ngx_signal_worker_processes(cycle,
                                         ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
@@ -232,7 +232,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy
             ngx_restart = 0;
             ngx_start_worker_processes(cycle, ccf->worker_processes,
                                        NGX_PROCESS_RESPAWN);
-            ngx_start_cleaner_process(cycle, NGX_PROCESS_RESPAWN);
+            ngx_start_cache_manager_process(cycle, NGX_PROCESS_RESPAWN);
             live = 1;
         }
 
@@ -360,7 +360,7 @@ ngx_start_worker_processes(ngx_cycle_t *
 
 
 static void
-ngx_start_cleaner_process(ngx_cycle_t *cycle, ngx_int_t type)
+ngx_start_cache_manager_process(ngx_cycle_t *cycle, ngx_int_t type)
 {
     ngx_int_t        i;
     ngx_uint_t       n;
@@ -369,7 +369,7 @@ ngx_start_cleaner_process(ngx_cycle_t *c
 
     path = ngx_cycle->pathes.elts;
     for (n = 0; n < ngx_cycle->pathes.nelts; n++) {
-        if (path[n]->cleaner) {
+        if (path[n]->manager) {
             goto start;
         }
     }
@@ -380,8 +380,8 @@ start:
 
     ch.command = NGX_CMD_OPEN_CHANNEL;
 
-    ngx_spawn_process(cycle, ngx_cleaner_process_cycle, NULL,
-                      "cleaner process", type);
+    ngx_spawn_process(cycle, ngx_cache_manager_process_cycle, NULL,
+                      "cache manager process", type);
 
     ch.pid = ngx_processes[ngx_process_slot].pid;
     ch.slot = ngx_process_slot;
@@ -1263,7 +1263,7 @@ ngx_worker_thread_cycle(void *data)
 
 
 static void
-ngx_cleaner_process_cycle(ngx_cycle_t *cycle, void *data)
+ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data)
 {
     void         *ident[4];
     ngx_event_t   ev;
@@ -1275,14 +1275,14 @@ ngx_cleaner_process_cycle(ngx_cycle_t *c
     ngx_close_listening_sockets(cycle);
 
     ngx_memzero(&ev, sizeof(ngx_event_t));
-    ev.handler = ngx_cleaner_process_handler;
+    ev.handler = ngx_cache_manager_process_handler;
     ev.data = ident;
     ev.log = cycle->log;
     ident[3] = (void *) -1;
 
     ngx_use_accept_mutex = 0;
 
-    ngx_setproctitle("cleaner process");
+    ngx_setproctitle("cache manager process");
 
     ngx_add_timer(&ev, 0);
 
@@ -1305,7 +1305,7 @@ ngx_cleaner_process_cycle(ngx_cycle_t *c
 
 
 static void
-ngx_cleaner_process_handler(ngx_event_t *ev)
+ngx_cache_manager_process_handler(ngx_event_t *ev)
 {
     time_t        next, n;
     ngx_uint_t    i;
@@ -1316,8 +1316,8 @@ ngx_cleaner_process_handler(ngx_event_t 
     path = ngx_cycle->pathes.elts;
     for (i = 0; i < ngx_cycle->pathes.nelts; i++) {
 
-        if (path[i]->cleaner) {
-            n = path[i]->cleaner(path[i]->data);
+        if (path[i]->manager) {
+            n = path[i]->manager(path[i]->data);
 
             next = (n <= next) ? n : next;
 
--- a/src/os/unix/ngx_solaris_config.h
+++ b/src/os/unix/ngx_solaris_config.h
@@ -28,6 +28,7 @@
 #include <grp.h>
 #include <dirent.h>
 #include <glob.h>
+#include <sys/statvfs.h>        /* statvfs() */
 
 #include <sys/filio.h>          /* FIONBIO */
 #include <sys/uio.h>