changeset 664:f5b859b2f097 NGINX_1_1_16

nginx 1.1.16 *) Change: the simultaneous subrequest limit has been raised to 200. *) Feature: the "from" parameter of the "disable_symlinks" directive. *) Feature: the "return" and "error_page" directives can be used to return 307 redirections. *) Bugfix: a segmentation fault might occur in a worker process if the "resolver" directive was used and there was no "error_log" directive specified at global level. Thanks to Roman Arutyunyan. *) Bugfix: a segmentation fault might occur in a worker process if the "proxy_http_version 1.1" or "fastcgi_keep_conn on" directives were used. *) Bugfix: memory leaks. Thanks to Lanshun Zhou. *) Bugfix: in the "disable_symlinks" directive. *) Bugfix: on ZFS filesystem disk cache size might be calculated incorrectly; the bug had appeared in 1.0.1. *) Bugfix: nginx could not be built by the icc 12.1 compiler. *) Bugfix: nginx could not be built by gcc on Solaris; the bug had appeared in 1.1.15.
author Igor Sysoev <http://sysoev.ru>
date Wed, 29 Feb 2012 00:00:00 +0400
parents dd668cf20818
children a8821023989f
files CHANGES CHANGES.ru auto/cc/name src/core/nginx.h src/core/ngx_open_file_cache.c src/core/ngx_open_file_cache.h src/core/ngx_resolver.c src/core/ngx_times.c src/event/ngx_event.h src/event/ngx_event_openssl.c src/event/ngx_event_pipe.c src/http/modules/ngx_http_autoindex_module.c src/http/modules/ngx_http_flv_module.c src/http/modules/ngx_http_gzip_static_module.c src/http/modules/ngx_http_headers_filter_module.c src/http/modules/ngx_http_index_module.c src/http/modules/ngx_http_limit_conn_module.c src/http/modules/ngx_http_limit_req_module.c src/http/modules/ngx_http_log_module.c src/http/modules/ngx_http_memcached_module.c src/http/modules/ngx_http_mp4_module.c src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_random_index_module.c src/http/modules/ngx_http_rewrite_module.c src/http/modules/ngx_http_static_module.c src/http/modules/ngx_http_userid_filter_module.c src/http/modules/perl/nginx.pm src/http/modules/perl/nginx.xs src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_file_cache.c src/http/ngx_http_header_filter_module.c src/http/ngx_http_parse_time.c src/http/ngx_http_request.h src/http/ngx_http_script.c src/http/ngx_http_special_response.c src/http/ngx_http_upstream.c src/mail/ngx_mail_pop3_handler.c src/os/unix/ngx_darwin_sendfile_chain.c src/os/unix/ngx_files.h src/os/unix/ngx_freebsd_sendfile_chain.c src/os/unix/ngx_process_cycle.c src/os/unix/ngx_user.c
diffstat 43 files changed, 564 insertions(+), 318 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,36 @@
 
+Changes with nginx 1.1.16                                        29 Feb 2012
+
+    *) Change: the simultaneous subrequest limit has been raised to 200.
+
+    *) Feature: the "from" parameter of the "disable_symlinks" directive.
+
+    *) Feature: the "return" and "error_page" directives can be used to
+       return 307 redirections.
+
+    *) Bugfix: a segmentation fault might occur in a worker process if the
+       "resolver" directive was used and there was no "error_log" directive
+       specified at global level.
+       Thanks to Roman Arutyunyan.
+
+    *) Bugfix: a segmentation fault might occur in a worker process if the
+       "proxy_http_version 1.1" or "fastcgi_keep_conn on" directives were
+       used.
+
+    *) Bugfix: memory leaks.
+       Thanks to Lanshun Zhou.
+
+    *) Bugfix: in the "disable_symlinks" directive.
+
+    *) Bugfix: on ZFS filesystem disk cache size might be calculated
+       incorrectly; the bug had appeared in 1.0.1.
+
+    *) Bugfix: nginx could not be built by the icc 12.1 compiler.
+
+    *) Bugfix: nginx could not be built by gcc on Solaris; the bug had
+       appeared in 1.1.15.
+
+
 Changes with nginx 1.1.15                                        15 Feb 2012
 
     *) Feature: the "disable_symlinks" directive.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,37 @@
 
+Изменения в nginx 1.1.16                                          29.02.2012
+
+    *) Изменение: ограничение на количество одновременных подзапросов
+       поднято до 200.
+
+    *) Добавление: параметр from в директиве disable_symlinks.
+
+    *) Добавление: директивы return и error_page теперь могут использоваться
+       для возврата перенаправлений с кодом 307.
+
+    *) Исправление: в рабочем процессе мог произойти segmentation fault,
+       если использовалась директива resolver и на глобальном уровне не была
+       задана директива error_log.
+       Спасибо Роману Арутюняну.
+
+    *) Исправление: в рабочем процессе мог произойти segmentation fault,
+       если использовались директивы "proxy_http_version 1.1" или
+       "fastcgi_keep_conn on".
+
+    *) Исправление: утечек памяти.
+       Спасибо Lanshun Zhou.
+
+    *) Исправление: в директиве disable_symlinks.
+
+    *) Исправление: при использовании ZFS размер кэша на диске мог считаться
+       некорректно; ошибка появилась в 1.0.1.
+
+    *) Исправление: nginx не собирался компилятором icc 12.1.
+
+    *) Исправление: nginx не собирался gcc на Solaris; ошибка появилась в
+       1.1.15.
+
+
 Изменения в nginx 1.1.15                                          15.02.2012
 
     *) Добавление: директива disable_symlinks.
--- a/auto/cc/name
+++ b/auto/cc/name
@@ -64,16 +64,16 @@ if [ "$CC" = bcc32 ]; then
     echo " + using Borland C++ compiler"
 
 else
+if `$CC -V 2>&1 | grep '^Intel(R) C' >/dev/null 2>&1`; then
+    NGX_CC_NAME=icc
+    echo " + using Intel C++ compiler"
+
+else
 if `$CC -v 2>&1 | grep 'gcc version' >/dev/null 2>&1`; then
     NGX_CC_NAME=gcc
     echo " + using GNU C compiler"
 
 else
-if `$CC -V 2>&1 | grep '^Intel(R) C' >/dev/null 2>&1`; then
-    NGX_CC_NAME=icc
-    echo " + using Intel C++ compiler"
-
-else
 if `$CC -V 2>&1 | grep 'Sun C' >/dev/null 2>&1`; then
     NGX_CC_NAME=sunc
     echo " + using Sun C compiler"
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -9,8 +9,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version      1001015
-#define NGINX_VERSION      "1.1.15"
+#define nginx_version      1001016
+#define NGINX_VERSION      "1.1.16"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -229,6 +229,7 @@ ngx_open_cached_file(ngx_open_file_cache
                 && now - file->created < of->valid
 #if (NGX_HAVE_OPENAT)
                 && of->disable_symlinks == file->disable_symlinks
+                && of->disable_symlinks_from == file->disable_symlinks_from
 #endif
             ))
         {
@@ -395,6 +396,7 @@ update:
     file->err = of->err;
 #if (NGX_HAVE_OPENAT)
     file->disable_symlinks = of->disable_symlinks;
+    file->disable_symlinks_from = of->disable_symlinks_from;
 #endif
 
     if (of->err == 0) {
@@ -504,8 +506,8 @@ ngx_openat_file_owner(ngx_fd_t at_fd, co
 
     fd = ngx_openat_file(at_fd, name, mode, create, access);
 
-    if (fd == NGX_FILE_ERROR) {
-        return NGX_FILE_ERROR;
+    if (fd == NGX_INVALID_FILE) {
+        return NGX_INVALID_FILE;
     }
 
     if (ngx_file_at_info(at_fd, name, &atfi, AT_SYMLINK_NOFOLLOW)
@@ -565,7 +567,6 @@ ngx_open_file_wrapper(ngx_str_t *name, n
     u_char           *p, *cp, *end;
     ngx_fd_t          at_fd;
     ngx_str_t         at_name;
-    ngx_file_info_t   fi;
 
     if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {
         fd = ngx_open_file(name->data, mode, create, access);
@@ -582,22 +583,45 @@ ngx_open_file_wrapper(ngx_str_t *name, n
     p = name->data;
     end = p + name->len;
 
-    at_fd = AT_FDCWD;
     at_name = *name;
 
-    if (p[0] == '/') {
-        at_fd = ngx_openat_file(at_fd, "/",
-                                NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
-                                NGX_FILE_OPEN, 0);
+    if (of->disable_symlinks_from) {
+
+        cp = p + of->disable_symlinks_from;
+
+        *cp = '\0';
+
+        at_fd = ngx_open_file(p, NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
+                              NGX_FILE_OPEN, 0);
+
+        *cp = '/';
 
-        if (at_fd == NGX_FILE_ERROR) {
+        if (at_fd == NGX_INVALID_FILE) {
+            of->err = ngx_errno;
+            of->failed = ngx_open_file_n;
+            return NGX_INVALID_FILE;
+        }
+
+        at_name.len = of->disable_symlinks_from;
+        p = cp + 1;
+
+    } else if (*p == '/') {
+
+        at_fd = ngx_open_file("/",
+                              NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
+                              NGX_FILE_OPEN, 0);
+
+        if (at_fd == NGX_INVALID_FILE) {
             of->err = ngx_errno;
             of->failed = ngx_openat_file_n;
-            return NGX_FILE_ERROR;
+            return NGX_INVALID_FILE;
         }
 
         at_name.len = 1;
         p++;
+
+    } else {
+        at_fd = NGX_AT_FDCWD;
     }
 
     for ( ;; ) {
@@ -615,12 +639,12 @@ ngx_open_file_wrapper(ngx_str_t *name, n
 
         if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) {
             fd = ngx_openat_file_owner(at_fd, p,
-                                       NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
+                                       NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
                                        NGX_FILE_OPEN, 0, log);
 
         } else {
             fd = ngx_openat_file(at_fd, p,
-                           NGX_FILE_RDONLY|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW,
+                           NGX_FILE_SEARCH|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW,
                            NGX_FILE_OPEN, 0);
         }
 
@@ -632,9 +656,9 @@ ngx_open_file_wrapper(ngx_str_t *name, n
             goto failed;
         }
 
-        if (at_fd != AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {
+        if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {
             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                          ngx_close_file_n " \"%V\" failed", at_name);
+                          ngx_close_file_n " \"%V\" failed", &at_name);
         }
 
         p = cp + 1;
@@ -642,47 +666,34 @@ ngx_open_file_wrapper(ngx_str_t *name, n
         at_name.len = cp - at_name.data;
     }
 
-    if (p == end && at_fd != AT_FDCWD) {
+    if (p == end) {
 
         /*
-         * If pathname ends with a trailing slash, check if last path
-         * component is a directory; if not, fail with ENOTDIR as per
-         * POSIX.
+         * If pathname ends with a trailing slash, assume the last path
+         * component is a directory and reopen it with requested flags;
+         * if not, fail with ENOTDIR as per POSIX.
          *
-         * We use separate check instead of O_DIRECTORY in the loop above,
-         * as O_DIRECTORY doesn't work on FreeBSD 8.
-         *
-         * Note this returns already opened file descriptor, with different
-         * mode/create/access.  This is believed to be safe as we don't
-         * use this codepath to create directories.
+         * We cannot rely on O_DIRECTORY in the loop above to check
+         * that the last path component is a directory because
+         * O_DIRECTORY doesn't work on FreeBSD 8.  Fortunately, by
+         * reopening a directory, we don't depend on it at all.
          */
 
-        if (ngx_fd_info(at_fd, &fi) == NGX_FILE_ERROR) {
-            of->err = ngx_errno;
-            of->failed = ngx_fd_info_n;
-            fd = NGX_INVALID_FILE;
-
-            goto failed;
-        }
-
-        if (ngx_is_dir(&fi)) {
-            return at_fd;
-        }
-
-        of->err = ENOTDIR;
-        of->failed = ngx_openat_file_n;
-        fd = NGX_INVALID_FILE;
-
-        goto failed;
+        fd = ngx_openat_file(at_fd, ".", mode, create, access);
+        goto done;
     }
 
-    if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) {
+    if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER
+        && !(create & (NGX_FILE_CREATE_OR_OPEN|NGX_FILE_TRUNCATE)))
+    {
         fd = ngx_openat_file_owner(at_fd, p, mode, create, access, log);
 
     } else {
         fd = ngx_openat_file(at_fd, p, mode|NGX_FILE_NOFOLLOW, create, access);
     }
 
+done:
+
     if (fd == NGX_INVALID_FILE) {
         of->err = ngx_errno;
         of->failed = ngx_openat_file_n;
@@ -690,9 +701,9 @@ ngx_open_file_wrapper(ngx_str_t *name, n
 
 failed:
 
-    if (at_fd != AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {
+    if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {
         ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                      ngx_close_file_n " \"%V\" failed", at_name);
+                      ngx_close_file_n " \"%V\" failed", &at_name);
     }
 
     return fd;
@@ -1131,20 +1142,15 @@ ngx_open_file_lookup(ngx_open_file_cache
 
         /* hash == node->key */
 
-        do {
-            file = (ngx_cached_open_file_t *) node;
+        file = (ngx_cached_open_file_t *) node;
 
-            rc = ngx_strcmp(name->data, file->name);
+        rc = ngx_strcmp(name->data, file->name);
 
-            if (rc == 0) {
-                return file;
-            }
+        if (rc == 0) {
+            return file;
+        }
 
-            node = (rc < 0) ? node->left : node->right;
-
-        } while (node != sentinel && hash == node->key);
-
-        break;
+        node = (rc < 0) ? node->left : node->right;
     }
 
     return NULL;
--- a/src/core/ngx_open_file_cache.h
+++ b/src/core/ngx_open_file_cache.h
@@ -33,6 +33,7 @@ typedef struct {
     ngx_uint_t               min_uses;
 
 #if (NGX_HAVE_OPENAT)
+    size_t                   disable_symlinks_from;
     unsigned                 disable_symlinks:2;
 #endif
 
@@ -69,6 +70,7 @@ struct ngx_cached_open_file_s {
     uint32_t                 uses;
 
 #if (NGX_HAVE_OPENAT)
+    size_t                   disable_symlinks_from;
     unsigned                 disable_symlinks:2;
 #endif
 
--- a/src/core/ngx_resolver.c
+++ b/src/core/ngx_resolver.c
@@ -189,11 +189,6 @@ ngx_resolver_create(ngx_conf_t *cf, ngx_
         uc->sockaddr = u.addrs->sockaddr;
         uc->socklen = u.addrs->socklen;
         uc->server = u.addrs->name;
-
-        uc->log = cf->cycle->new_log;
-        uc->log.handler = ngx_resolver_log_error;
-        uc->log.data = uc;
-        uc->log.action = "resolving";
     }
 
     return r;
@@ -876,6 +871,12 @@ ngx_resolver_send_query(ngx_resolver_t *
     }
 
     if (uc->connection == NULL) {
+
+        uc->log = *r->log;
+        uc->log.handler = ngx_resolver_log_error;
+        uc->log.data = uc;
+        uc->log.action = "resolving";
+
         if (ngx_udp_connect(uc) != NGX_OK) {
             return NGX_ERROR;
         }
@@ -1688,20 +1689,15 @@ ngx_resolver_lookup_name(ngx_resolver_t 
 
         /* hash == node->key */
 
-        do {
-            rn = (ngx_resolver_node_t *) node;
-
-            rc = ngx_memn2cmp(name->data, rn->name, name->len, rn->nlen);
-
-            if (rc == 0) {
-                return rn;
-            }
-
-            node = (rc < 0) ? node->left : node->right;
-
-        } while (node != sentinel && hash == node->key);
-
-        break;
+        rn = (ngx_resolver_node_t *) node;
+
+        rc = ngx_memn2cmp(name->data, rn->name, name->len, rn->nlen);
+
+        if (rc == 0) {
+            return rn;
+        }
+
+        node = (rc < 0) ? node->left : node->right;
     }
 
     /* not found */
--- a/src/core/ngx_times.c
+++ b/src/core/ngx_times.c
@@ -287,7 +287,7 @@ ngx_gmtime(time_t t, ngx_tm_t *tp)
 
     days = n / 86400;
 
-    /* Jaunary 1, 1970 was Thursday */
+    /* January 1, 1970 was Thursday */
 
     wday = (4 + days) % 7;
 
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -83,7 +83,7 @@ struct ngx_event_s {
 #endif
 
 #if (NGX_WIN32)
-    /* setsockopt(SO_UPDATE_ACCEPT_CONTEXT) was succesfull */
+    /* setsockopt(SO_UPDATE_ACCEPT_CONTEXT) was successful */
     unsigned         accept_context_updated:1;
 #endif
 
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -842,7 +842,7 @@ ngx_ssl_recv(ngx_connection_t *c, u_char
         case NGX_ERROR:
             c->read->error = 1;
 
-            /* fall thruogh */
+            /* fall through */
 
         case NGX_AGAIN:
             return c->ssl->last;
@@ -1801,44 +1801,39 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_
 
         /* hash == node->key */
 
-        do {
-            sess_id = (ngx_ssl_sess_id_t *) node;
-
-            rc = ngx_memn2cmp(id, sess_id->id,
-                              (size_t) len, (size_t) node->data);
-            if (rc == 0) {
-
-                if (sess_id->expire > ngx_time()) {
-                    ngx_memcpy(buf, sess_id->session, sess_id->len);
-
-                    ngx_shmtx_unlock(&shpool->mutex);
-
-                    p = buf;
-                    sess = d2i_SSL_SESSION(NULL, &p, sess_id->len);
-
-                    return sess;
-                }
-
-                ngx_queue_remove(&sess_id->queue);
-
-                ngx_rbtree_delete(&cache->session_rbtree, node);
-
-                ngx_slab_free_locked(shpool, sess_id->session);
+        sess_id = (ngx_ssl_sess_id_t *) node;
+
+        rc = ngx_memn2cmp(id, sess_id->id, (size_t) len, (size_t) node->data);
+
+        if (rc == 0) {
+
+            if (sess_id->expire > ngx_time()) {
+                ngx_memcpy(buf, sess_id->session, sess_id->len);
+
+                ngx_shmtx_unlock(&shpool->mutex);
+
+                p = buf;
+                sess = d2i_SSL_SESSION(NULL, &p, sess_id->len);
+
+                return sess;
+            }
+
+            ngx_queue_remove(&sess_id->queue);
+
+            ngx_rbtree_delete(&cache->session_rbtree, node);
+
+            ngx_slab_free_locked(shpool, sess_id->session);
 #if (NGX_PTR_SIZE == 4)
-                ngx_slab_free_locked(shpool, sess_id->id);
+            ngx_slab_free_locked(shpool, sess_id->id);
 #endif
-                ngx_slab_free_locked(shpool, sess_id);
-
-                sess = NULL;
-
-                goto done;
-            }
-
-            node = (rc < 0) ? node->left : node->right;
-
-        } while (node != sentinel && hash == node->key);
-
-        break;
+            ngx_slab_free_locked(shpool, sess_id);
+
+            sess = NULL;
+
+            goto done;
+        }
+
+        node = (rc < 0) ? node->left : node->right;
     }
 
 done:
@@ -1908,31 +1903,26 @@ ngx_ssl_remove_session(SSL_CTX *ssl, ngx
 
         /* hash == node->key */
 
-        do {
-            sess_id = (ngx_ssl_sess_id_t *) node;
-
-            rc = ngx_memn2cmp(id, sess_id->id, len, (size_t) node->data);
-
-            if (rc == 0) {
-
-                ngx_queue_remove(&sess_id->queue);
-
-                ngx_rbtree_delete(&cache->session_rbtree, node);
-
-                ngx_slab_free_locked(shpool, sess_id->session);
+        sess_id = (ngx_ssl_sess_id_t *) node;
+
+        rc = ngx_memn2cmp(id, sess_id->id, len, (size_t) node->data);
+
+        if (rc == 0) {
+
+            ngx_queue_remove(&sess_id->queue);
+
+            ngx_rbtree_delete(&cache->session_rbtree, node);
+
+            ngx_slab_free_locked(shpool, sess_id->session);
 #if (NGX_PTR_SIZE == 4)
-                ngx_slab_free_locked(shpool, sess_id->id);
+            ngx_slab_free_locked(shpool, sess_id->id);
 #endif
-                ngx_slab_free_locked(shpool, sess_id);
-
-                goto done;
-            }
-
-            node = (rc < 0) ? node->left : node->right;
-
-        } while (node != sentinel && hash == node->key);
-
-        break;
+            ngx_slab_free_locked(shpool, sess_id);
+
+            goto done;
+        }
+
+        node = (rc < 0) ? node->left : node->right;
     }
 
 done:
--- a/src/event/ngx_event_pipe.c
+++ b/src/event/ngx_event_pipe.c
@@ -401,13 +401,14 @@ ngx_event_pipe_read_upstream(ngx_event_p
 
         if (cl->buf->last - cl->buf->pos >= p->length) {
 
+            p->free_raw_bufs = cl->next;
+
             /* STUB */ cl->buf->num = p->num++;
 
             if (p->input_filter(p, cl->buf) == NGX_ERROR) {
                  return NGX_ABORT;
             }
 
-            p->free_raw_bufs = cl->next;
             ngx_free_chain(p->pool, cl);
         }
     }
@@ -968,7 +969,7 @@ ngx_event_pipe_add_free_buf(ngx_event_pi
         return NGX_OK;
     }
 
-    /* the first free buf is partialy filled, thus add the free buf after it */
+    /* the first free buf is partially filled, thus add the free buf after it */
 
     cl->next = p->free_raw_bufs->next;
     p->free_raw_bufs->next = cl;
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -95,8 +95,8 @@ static ngx_http_module_t  ngx_http_autoi
     NULL,                                  /* create server configuration */
     NULL,                                  /* merge server configuration */
 
-    ngx_http_autoindex_create_loc_conf,    /* create location configration */
-    ngx_http_autoindex_merge_loc_conf      /* merge location configration */
+    ngx_http_autoindex_create_loc_conf,    /* create location configuration */
+    ngx_http_autoindex_merge_loc_conf      /* merge location configuration */
 };
 
 
--- a/src/http/modules/ngx_http_flv_module.c
+++ b/src/http/modules/ngx_http_flv_module.c
@@ -109,9 +109,10 @@ ngx_http_flv_handler(ngx_http_request_t 
     of.min_uses = clcf->open_file_cache_min_uses;
     of.errors = clcf->open_file_cache_errors;
     of.events = clcf->open_file_cache_events;
-#if (NGX_HAVE_OPENAT)
-    of.disable_symlinks = clcf->disable_symlinks;
-#endif
+
+    if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
 
     if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
         != NGX_OK)
--- a/src/http/modules/ngx_http_gzip_static_module.c
+++ b/src/http/modules/ngx_http_gzip_static_module.c
@@ -129,9 +129,10 @@ ngx_http_gzip_static_handler(ngx_http_re
     of.min_uses = clcf->open_file_cache_min_uses;
     of.errors = clcf->open_file_cache_errors;
     of.events = clcf->open_file_cache_events;
-#if (NGX_HAVE_OPENAT)
-    of.disable_symlinks = clcf->disable_symlinks;
-#endif
+
+    if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
 
     if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
         != NGX_OK)
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -149,7 +149,9 @@ ngx_http_headers_filter(ngx_http_request
             && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT
             && r->headers_out.status != NGX_HTTP_MOVED_PERMANENTLY
             && r->headers_out.status != NGX_HTTP_MOVED_TEMPORARILY
-            && r->headers_out.status != NGX_HTTP_NOT_MODIFIED))
+            && r->headers_out.status != NGX_HTTP_SEE_OTHER
+            && r->headers_out.status != NGX_HTTP_NOT_MODIFIED
+            && r->headers_out.status != NGX_HTTP_TEMPORARY_REDIRECT))
     {
         return ngx_http_next_header_filter(r);
     }
--- a/src/http/modules/ngx_http_index_module.c
+++ b/src/http/modules/ngx_http_index_module.c
@@ -62,8 +62,8 @@ static ngx_http_module_t  ngx_http_index
     NULL,                                  /* create server configuration */
     NULL,                                  /* merge server configuration */
 
-    ngx_http_index_create_loc_conf,        /* create location configration */
-    ngx_http_index_merge_loc_conf          /* merge location configration */
+    ngx_http_index_create_loc_conf,        /* create location configuration */
+    ngx_http_index_merge_loc_conf          /* merge location configuration */
 };
 
 
@@ -209,9 +209,10 @@ ngx_http_index_handler(ngx_http_request_
         of.test_only = 1;
         of.errors = clcf->open_file_cache_errors;
         of.events = clcf->open_file_cache_events;
-#if (NGX_HAVE_OPENAT)
-        of.disable_symlinks = clcf->disable_symlinks;
-#endif
+
+        if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
 
         if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
             != NGX_OK)
@@ -307,9 +308,10 @@ ngx_http_index_test_dir(ngx_http_request
     of.test_only = 1;
     of.valid = clcf->open_file_cache_valid;
     of.errors = clcf->open_file_cache_errors;
-#if (NGX_HAVE_OPENAT)
-    of.disable_symlinks = clcf->disable_symlinks;
-#endif
+
+    if (ngx_http_set_disable_symlinks(r, clcf, &dir, &of) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
 
     if (ngx_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool)
         != NGX_OK)
--- a/src/http/modules/ngx_http_limit_conn_module.c
+++ b/src/http/modules/ngx_http_limit_conn_module.c
@@ -118,8 +118,8 @@ static ngx_http_module_t  ngx_http_limit
     NULL,                                  /* create server configuration */
     NULL,                                  /* merge server configuration */
 
-    ngx_http_limit_conn_create_conf,       /* create location configration */
-    ngx_http_limit_conn_merge_conf         /* merge location configration */
+    ngx_http_limit_conn_create_conf,       /* create location configuration */
+    ngx_http_limit_conn_merge_conf         /* merge location configuration */
 };
 
 
@@ -325,20 +325,15 @@ ngx_http_limit_conn_lookup(ngx_rbtree_t 
 
         /* hash == node->key */
 
-        do {
-            lcn = (ngx_http_limit_conn_node_t *) &node->color;
+        lcn = (ngx_http_limit_conn_node_t *) &node->color;
 
-            rc = ngx_memn2cmp(vv->data, lcn->data,
-                              (size_t) vv->len, (size_t) lcn->len);
-            if (rc == 0) {
-                return node;
-            }
+        rc = ngx_memn2cmp(vv->data, lcn->data,
+                          (size_t) vv->len, (size_t) lcn->len);
+        if (rc == 0) {
+            return node;
+        }
 
-            node = (rc < 0) ? node->left : node->right;
-
-        } while (node != sentinel && hash == node->key);
-
-        break;
+        node = (rc < 0) ? node->left : node->right;
     }
 
     return NULL;
--- a/src/http/modules/ngx_http_limit_req_module.c
+++ b/src/http/modules/ngx_http_limit_req_module.c
@@ -121,8 +121,8 @@ static ngx_http_module_t  ngx_http_limit
     NULL,                                  /* create server configuration */
     NULL,                                  /* merge server configuration */
 
-    ngx_http_limit_req_create_conf,        /* create location configration */
-    ngx_http_limit_req_merge_conf          /* merge location configration */
+    ngx_http_limit_req_create_conf,        /* create location configuration */
+    ngx_http_limit_req_merge_conf          /* merge location configuration */
 };
 
 
@@ -385,47 +385,42 @@ ngx_http_limit_req_lookup(ngx_http_limit
 
         /* hash == node->key */
 
-        do {
-            lr = (ngx_http_limit_req_node_t *) &node->color;
-
-            rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len);
+        lr = (ngx_http_limit_req_node_t *) &node->color;
 
-            if (rc == 0) {
-                ngx_queue_remove(&lr->queue);
-                ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
-
-                ms = (ngx_msec_int_t) (now - lr->last);
-
-                excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
-
-                if (excess < 0) {
-                    excess = 0;
-                }
+        rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len);
 
-                *ep = excess;
-
-                if ((ngx_uint_t) excess > limit->burst) {
-                    return NGX_BUSY;
-                }
+        if (rc == 0) {
+            ngx_queue_remove(&lr->queue);
+            ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
 
-                if (account) {
-                    lr->excess = excess;
-                    lr->last = now;
-                    return NGX_OK;
-                }
+            ms = (ngx_msec_int_t) (now - lr->last);
 
-                lr->count++;
+            excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
 
-                ctx->node = lr;
-
-                return NGX_AGAIN;
+            if (excess < 0) {
+                excess = 0;
             }
 
-            node = (rc < 0) ? node->left : node->right;
+            *ep = excess;
+
+            if ((ngx_uint_t) excess > limit->burst) {
+                return NGX_BUSY;
+            }
 
-        } while (node != sentinel && hash == node->key);
+            if (account) {
+                lr->excess = excess;
+                lr->last = now;
+                return NGX_OK;
+            }
 
-        break;
+            lr->count++;
+
+            ctx->node = lr;
+
+            return NGX_AGAIN;
+        }
+
+        node = (rc < 0) ? node->left : node->right;
     }
 
     *ep = 0;
--- a/src/http/modules/ngx_http_log_module.c
+++ b/src/http/modules/ngx_http_log_module.c
@@ -161,8 +161,8 @@ static ngx_http_module_t  ngx_http_log_m
     NULL,                                  /* create server configuration */
     NULL,                                  /* merge server configuration */
 
-    ngx_http_log_create_loc_conf,          /* create location configration */
-    ngx_http_log_merge_loc_conf            /* merge location configration */
+    ngx_http_log_create_loc_conf,          /* create location configuration */
+    ngx_http_log_merge_loc_conf            /* merge location configuration */
 };
 
 
@@ -377,10 +377,10 @@ ngx_http_log_script_write(ngx_http_reque
 
     if (!r->root_tested) {
 
-        /* test root directory existance */
+        /* test root directory existence */
 
         if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
-            /* simulate successfull logging */
+            /* simulate successful logging */
             return len;
         }
 
@@ -394,22 +394,24 @@ ngx_http_log_script_write(ngx_http_reque
         of.test_only = 1;
         of.errors = clcf->open_file_cache_errors;
         of.events = clcf->open_file_cache_events;
-#if (NGX_HAVE_OPENAT)
-        of.disable_symlinks = clcf->disable_symlinks;
-#endif
+
+        if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+            /* simulate successful logging */
+            return len;
+        }
 
         if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
             != NGX_OK)
         {
             if (of.err == 0) {
-                /* simulate successfull logging */
+                /* simulate successful logging */
                 return len;
             }
 
             ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err,
                           "testing \"%s\" existence failed", path.data);
 
-            /* simulate successfull logging */
+            /* simulate successful logging */
             return len;
         }
 
@@ -417,7 +419,7 @@ ngx_http_log_script_write(ngx_http_reque
             ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ENOTDIR,
                           "testing \"%s\" existence failed", path.data);
 
-            /* simulate successfull logging */
+            /* simulate successful logging */
             return len;
         }
     }
@@ -426,7 +428,7 @@ ngx_http_log_script_write(ngx_http_reque
                             script->values->elts)
         == NULL)
     {
-        /* simulate successfull logging */
+        /* simulate successful logging */
         return len;
     }
 
@@ -444,16 +446,18 @@ ngx_http_log_script_write(ngx_http_reque
     of.valid = llcf->open_file_cache_valid;
     of.min_uses = llcf->open_file_cache_min_uses;
     of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
-#if (NGX_HAVE_OPENAT)
-    of.disable_symlinks = clcf->disable_symlinks;
-#endif
+
+    if (ngx_http_set_disable_symlinks(r, clcf, &log, &of) != NGX_OK) {
+        /* simulate successful logging */
+        return len;
+    }
 
     if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool)
         != NGX_OK)
     {
         ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                       "%s \"%s\" failed", of.failed, log.data);
-        /* simulate successfull logging */
+        /* simulate successful logging */
         return len;
     }
 
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -115,8 +115,8 @@ static ngx_http_module_t  ngx_http_memca
     NULL,                                  /* create server configuration */
     NULL,                                  /* merge server configuration */
 
-    ngx_http_memcached_create_loc_conf,    /* create location configration */
-    ngx_http_memcached_merge_loc_conf      /* merge location configration */
+    ngx_http_memcached_create_loc_conf,    /* create location configuration */
+    ngx_http_memcached_merge_loc_conf      /* merge location configuration */
 };
 
 
--- a/src/http/modules/ngx_http_mp4_module.c
+++ b/src/http/modules/ngx_http_mp4_module.c
@@ -440,9 +440,10 @@ ngx_http_mp4_handler(ngx_http_request_t 
     of.min_uses = clcf->open_file_cache_min_uses;
     of.errors = clcf->open_file_cache_errors;
     of.events = clcf->open_file_cache_events;
-#if (NGX_HAVE_OPENAT)
-    of.disable_symlinks = clcf->disable_symlinks;
-#endif
+
+    if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
 
     if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
         != NGX_OK)
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -530,8 +530,8 @@ static ngx_http_module_t  ngx_http_proxy
     NULL,                                  /* create server configuration */
     NULL,                                  /* merge server configuration */
 
-    ngx_http_proxy_create_loc_conf,        /* create location configration */
-    ngx_http_proxy_merge_loc_conf          /* merge location configration */
+    ngx_http_proxy_create_loc_conf,        /* create location configuration */
+    ngx_http_proxy_merge_loc_conf          /* merge location configuration */
 };
 
 
--- a/src/http/modules/ngx_http_random_index_module.c
+++ b/src/http/modules/ngx_http_random_index_module.c
@@ -49,8 +49,8 @@ static ngx_http_module_t  ngx_http_rando
     NULL,                                  /* create server configuration */
     NULL,                                  /* merge server configuration */
 
-    ngx_http_random_index_create_loc_conf, /* create location configration */
-    ngx_http_random_index_merge_loc_conf   /* merge location configration */
+    ngx_http_random_index_create_loc_conf, /* create location configuration */
+    ngx_http_random_index_merge_loc_conf   /* merge location configuration */
 };
 
 
--- a/src/http/modules/ngx_http_rewrite_module.c
+++ b/src/http/modules/ngx_http_rewrite_module.c
@@ -112,8 +112,8 @@ static ngx_http_module_t  ngx_http_rewri
     NULL,                                  /* create server configuration */
     NULL,                                  /* merge server configuration */
 
-    ngx_http_rewrite_create_loc_conf,      /* create location configration */
-    ngx_http_rewrite_merge_loc_conf        /* merge location configration */
+    ngx_http_rewrite_create_loc_conf,      /* create location configuration */
+    ngx_http_rewrite_merge_loc_conf        /* merge location configuration */
 };
 
 
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -94,9 +94,10 @@ ngx_http_static_handler(ngx_http_request
     of.min_uses = clcf->open_file_cache_min_uses;
     of.errors = clcf->open_file_cache_errors;
     of.events = clcf->open_file_cache_events;
-#if (NGX_HAVE_OPENAT)
-    of.disable_symlinks = clcf->disable_symlinks;
-#endif
+
+    if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
 
     if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
         != NGX_OK)
--- a/src/http/modules/ngx_http_userid_filter_module.c
+++ b/src/http/modules/ngx_http_userid_filter_module.c
@@ -166,8 +166,8 @@ static ngx_http_module_t  ngx_http_useri
     NULL,                                  /* create server configuration */
     NULL,                                  /* merge server configuration */
 
-    ngx_http_userid_create_conf,           /* create location configration */
-    ngx_http_userid_merge_conf             /* merge location configration */
+    ngx_http_userid_create_conf,           /* create location configuration */
+    ngx_http_userid_merge_conf             /* merge location configuration */
 };
 
 
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -21,7 +21,9 @@ our @EXPORT = qw(
     HTTP_MOVED_PERMANENTLY
     HTTP_MOVED_TEMPORARILY
     HTTP_REDIRECT
+    HTTP_SEE_OTHER
     HTTP_NOT_MODIFIED
+    HTTP_TEMPORARY_REDIRECT
 
     HTTP_BAD_REQUEST
     HTTP_UNAUTHORIZED
@@ -48,7 +50,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '1.1.15';
+our $VERSION = '1.1.16';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
@@ -67,7 +69,9 @@ use constant HTTP_PARTIAL_CONTENT       
 use constant HTTP_MOVED_PERMANENTLY         => 301;
 use constant HTTP_MOVED_TEMPORARILY         => 302;
 use constant HTTP_REDIRECT                  => 302;
+use constant HTTP_SEE_OTHER                 => 303;
 use constant HTTP_NOT_MODIFIED              => 304;
+use constant HTTP_TEMPORARY_REDIRECT        => 307;
 
 use constant HTTP_BAD_REQUEST               => 400;
 use constant HTTP_UNAUTHORIZED              => 401;
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -662,9 +662,10 @@ sendfile(r, filename, offset = -1, bytes
     of.min_uses = clcf->open_file_cache_min_uses;
     of.errors = clcf->open_file_cache_errors;
     of.events = clcf->open_file_cache_events;
-#if (NGX_HAVE_OPENAT)
-    of.disable_symlinks = clcf->disable_symlinks;
-#endif
+
+    if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+        XSRETURN_EMPTY;
+    }
 
     if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
         != NGX_OK)
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -76,6 +76,10 @@ static ngx_uint_t ngx_http_gzip_quantity
 static char *ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 #endif
+#if (NGX_HAVE_OPENAT)
+static char *ngx_http_disable_symlinks(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+#endif
 
 static char *ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data);
 static char *ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data);
@@ -187,18 +191,6 @@ static ngx_str_t  ngx_http_gzip_private 
 #endif
 
 
-#if (NGX_HAVE_OPENAT)
-
-static ngx_conf_enum_t  ngx_http_core_disable_symlinks[] = {
-    { ngx_string("off"), NGX_DISABLE_SYMLINKS_OFF },
-    { ngx_string("if_not_owner"), NGX_DISABLE_SYMLINKS_NOTOWNER },
-    { ngx_string("on"), NGX_DISABLE_SYMLINKS_ON },
-    { ngx_null_string, 0 }
-};
-
-#endif
-
-
 static ngx_command_t  ngx_http_core_commands[] = {
 
     { ngx_string("variables_hash_max_size"),
@@ -779,11 +771,11 @@ static ngx_command_t  ngx_http_core_comm
 #if (NGX_HAVE_OPENAT)
 
     { ngx_string("disable_symlinks"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_enum_slot,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
+      ngx_http_disable_symlinks,
       NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_core_loc_conf_t, disable_symlinks),
-      &ngx_http_core_disable_symlinks },
+      0,
+      NULL },
 
 #endif
 
@@ -1320,9 +1312,11 @@ ngx_http_core_try_files_phase(ngx_http_r
         of.test_only = 1;
         of.errors = clcf->open_file_cache_errors;
         of.events = clcf->open_file_cache_events;
-#if (NGX_HAVE_OPENAT)
-        of.disable_symlinks = clcf->disable_symlinks;
-#endif
+
+        if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return NGX_OK;
+        }
 
         if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
             != NGX_OK)
@@ -1824,8 +1818,11 @@ ngx_http_send_response(ngx_http_request_
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    if (status >= NGX_HTTP_MOVED_PERMANENTLY && status <= NGX_HTTP_SEE_OTHER) {
-
+    if (status == NGX_HTTP_MOVED_PERMANENTLY
+        || status == NGX_HTTP_MOVED_TEMPORARILY
+        || status == NGX_HTTP_SEE_OTHER
+        || status == NGX_HTTP_TEMPORARY_REDIRECT)
+    {
         ngx_http_clear_location(r);
 
         r->headers_out.location = ngx_list_push(&r->headers_out.headers);
@@ -2642,6 +2639,56 @@ ngx_http_cleanup_add(ngx_http_request_t 
 }
 
 
+ngx_int_t
+ngx_http_set_disable_symlinks(ngx_http_request_t *r,
+    ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of)
+{
+#if (NGX_HAVE_OPENAT)
+    u_char     *p;
+    ngx_str_t   from;
+
+    of->disable_symlinks = clcf->disable_symlinks;
+
+    if (clcf->disable_symlinks_from == NULL) {
+        return NGX_OK;
+    }
+
+    if (ngx_http_complex_value(r, clcf->disable_symlinks_from, &from)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    if (from.len == 0
+        || from.len > path->len
+        || ngx_memcmp(path->data, from.data, from.len) != 0)
+    {
+        return NGX_OK;
+    }
+
+    if (from.len == path->len) {
+        of->disable_symlinks = NGX_DISABLE_SYMLINKS_OFF;
+        return NGX_OK;
+    }
+
+    p = path->data + from.len;
+
+    if (*p == '/') {
+        of->disable_symlinks_from = from.len;
+        return NGX_OK;
+    }
+
+    p--;
+
+    if (*p == '/') {
+        of->disable_symlinks_from = from.len - 1;
+    }
+#endif
+
+    return NGX_OK;
+}
+
+
 static char *
 ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
 {
@@ -3372,6 +3419,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t
 
 #if (NGX_HAVE_OPENAT)
     clcf->disable_symlinks = NGX_CONF_UNSET_UINT;
+    clcf->disable_symlinks_from = NGX_CONF_UNSET_PTR;
 #endif
 
     return clcf;
@@ -3656,6 +3704,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
 #if (NGX_HAVE_OPENAT)
     ngx_conf_merge_uint_value(conf->disable_symlinks, prev->disable_symlinks,
                               NGX_DISABLE_SYMLINKS_OFF);
+    ngx_conf_merge_ptr_value(conf->disable_symlinks_from,
+                             prev->disable_symlinks_from, NULL);
 #endif
 
     return NGX_CONF_OK;
@@ -4791,6 +4841,100 @@ ngx_http_gzip_disable(ngx_conf_t *cf, ng
 #endif
 
 
+#if (NGX_HAVE_OPENAT)
+
+static char *
+ngx_http_disable_symlinks(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_core_loc_conf_t *clcf = conf;
+
+    ngx_str_t                         *value;
+    ngx_uint_t                         i;
+    ngx_http_compile_complex_value_t   ccv;
+
+    if (clcf->disable_symlinks != NGX_CONF_UNSET_UINT) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        if (ngx_strcmp(value[i].data, "off") == 0) {
+            clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_OFF;
+            continue;
+        }
+
+        if (ngx_strcmp(value[i].data, "if_not_owner") == 0) {
+            clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_NOTOWNER;
+            continue;
+        }
+
+        if (ngx_strcmp(value[i].data, "on") == 0) {
+            clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_ON;
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "from=", 5) == 0) {
+            value[i].len -= 5;
+            value[i].data += 5;
+
+            ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+            ccv.cf = cf;
+            ccv.value = &value[i];
+            ccv.complex_value = ngx_palloc(cf->pool,
+                                           sizeof(ngx_http_complex_value_t));
+            if (ccv.complex_value == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+                return NGX_CONF_ERROR;
+            }
+
+            clcf->disable_symlinks_from = ccv.complex_value;
+
+            continue;
+        }
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid parameter \"%V\"", &value[i]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (clcf->disable_symlinks == NGX_CONF_UNSET_UINT) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"%V\" must have \"off\", \"on\" "
+                           "or \"if_not_owner\" parameter",
+                           &cmd->name);
+        return NGX_CONF_ERROR;
+    }
+
+    if (cf->args->nelts == 2) {
+        clcf->disable_symlinks_from = NULL;
+        return NGX_CONF_OK;
+    }
+
+    if (clcf->disable_symlinks_from == NGX_CONF_UNSET_PTR) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "duplicate parameters \"%V %V\"",
+                           &value[1], &value[2]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (clcf->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"from=\" cannot be used with \"off\" parameter");
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+#endif
+
+
 static char *
 ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data)
 {
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -406,6 +406,7 @@ struct ngx_http_core_loc_conf_s {
 
 #if (NGX_HAVE_OPENAT)
     ngx_uint_t    disable_symlinks;        /* disable_symlinks */
+    ngx_http_complex_value_t  *disable_symlinks_from;
 #endif
 
     ngx_array_t  *error_pages;             /* error_page */
@@ -509,6 +510,10 @@ ngx_int_t ngx_http_output_filter(ngx_htt
 ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *chain);
 
 
+ngx_int_t ngx_http_set_disable_symlinks(ngx_http_request_t *r,
+    ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of);
+
+
 extern ngx_module_t  ngx_http_core_module;
 
 extern ngx_uint_t ngx_http_max_module;
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -799,21 +799,16 @@ ngx_http_file_cache_lookup(ngx_http_file
 
         /* node_key == node->key */
 
-        do {
-            fcn = (ngx_http_file_cache_node_t *) node;
+        fcn = (ngx_http_file_cache_node_t *) node;
 
-            rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key,
-                            NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
+        rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key,
+                        NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
 
-            if (rc == 0) {
-                return fcn;
-            }
+        if (rc == 0) {
+            return fcn;
+        }
 
-            node = (rc < 0) ? node->left : node->right;
-
-        } while (node != sentinel && node_key == node->key);
-
-        break;
+        node = (rc < 0) ? node->left : node->right;
     }
 
     /* not found */
--- a/src/http/ngx_http_header_filter_module.c
+++ b/src/http/ngx_http_header_filter_module.c
@@ -71,12 +71,11 @@ static ngx_str_t ngx_http_status_lines[]
     ngx_string("302 Moved Temporarily"),
     ngx_string("303 See Other"),
     ngx_string("304 Not Modified"),
+    ngx_null_string,  /* "305 Use Proxy" */
+    ngx_null_string,  /* "306 unused" */
+    ngx_string("307 Temporary Redirect"),
 
-    /* ngx_null_string, */  /* "305 Use Proxy" */
-    /* ngx_null_string, */  /* "306 unused" */
-    /* ngx_null_string, */  /* "307 Temporary Redirect" */
-
-#define NGX_HTTP_LAST_3XX  305
+#define NGX_HTTP_LAST_3XX  308
 #define NGX_HTTP_OFF_4XX   (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX)
 
     ngx_string("400 Bad Request"),
--- a/src/http/ngx_http_parse_time.c
+++ b/src/http/ngx_http_parse_time.c
@@ -242,7 +242,7 @@ ngx_http_parse_time(u_char *value, size_
         year -= 1;
     }
 
-    /* Gauss' formula for Grigorian days since March 1, 1 BC */
+    /* Gauss' formula for Gregorian days since March 1, 1 BC */
 
     time = (uint64_t) (
             /* days in years including leap years since March 1, 1 BC */
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -10,7 +10,7 @@
 
 
 #define NGX_HTTP_MAX_URI_CHANGES           10
-#define NGX_HTTP_MAX_SUBREQUESTS           50
+#define NGX_HTTP_MAX_SUBREQUESTS           200
 
 /* must be 2^n */
 #define NGX_HTTP_LC_HEADER_LEN             32
@@ -75,6 +75,7 @@
 #define NGX_HTTP_MOVED_TEMPORARILY         302
 #define NGX_HTTP_SEE_OTHER                 303
 #define NGX_HTTP_NOT_MODIFIED              304
+#define NGX_HTTP_TEMPORARY_REDIRECT        307
 
 #define NGX_HTTP_BAD_REQUEST               400
 #define NGX_HTTP_UNAUTHORIZED              401
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -1505,9 +1505,12 @@ ngx_http_script_file_code(ngx_http_scrip
     of.test_only = 1;
     of.errors = clcf->open_file_cache_errors;
     of.events = clcf->open_file_cache_events;
-#if (NGX_HAVE_OPENAT)
-    of.disable_symlinks = clcf->disable_symlinks;
-#endif
+
+    if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+        e->ip = ngx_http_script_exit;
+        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+        return;
+    }
 
     if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
         != NGX_OK)
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -74,6 +74,14 @@ static char ngx_http_error_303_page[] =
 ;
 
 
+static char ngx_http_error_307_page[] =
+"<html>" CRLF
+"<head><title>307 Temporary Redirect</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>307 Temporary Redirect</h1></center>" CRLF
+;
+
+
 static char ngx_http_error_400_page[] =
 "<html>" CRLF
 "<head><title>400 Bad Request</title></head>" CRLF
@@ -294,16 +302,20 @@ static ngx_str_t ngx_http_error_pages[] 
 
     ngx_null_string,                     /* 201, 204 */
 
-#define NGX_HTTP_LAST_LEVEL_200  202
-#define NGX_HTTP_LEVEL_200       (NGX_HTTP_LAST_LEVEL_200 - 201)
+#define NGX_HTTP_LAST_2XX  202
+#define NGX_HTTP_OFF_3XX   (NGX_HTTP_LAST_2XX - 201)
 
     /* ngx_null_string, */               /* 300 */
     ngx_string(ngx_http_error_301_page),
     ngx_string(ngx_http_error_302_page),
     ngx_string(ngx_http_error_303_page),
+    ngx_null_string,                     /* 304 */
+    ngx_null_string,                     /* 305 */
+    ngx_null_string,                     /* 306 */
+    ngx_string(ngx_http_error_307_page),
 
-#define NGX_HTTP_LAST_LEVEL_300  304
-#define NGX_HTTP_LEVEL_300       (NGX_HTTP_LAST_LEVEL_300 - 301)
+#define NGX_HTTP_LAST_3XX  308
+#define NGX_HTTP_OFF_4XX   (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX)
 
     ngx_string(ngx_http_error_400_page),
     ngx_string(ngx_http_error_401_page),
@@ -323,8 +335,8 @@ static ngx_str_t ngx_http_error_pages[] 
     ngx_string(ngx_http_error_415_page),
     ngx_string(ngx_http_error_416_page),
 
-#define NGX_HTTP_LAST_LEVEL_400  417
-#define NGX_HTTP_LEVEL_400       (NGX_HTTP_LAST_LEVEL_400 - 400)
+#define NGX_HTTP_LAST_4XX  417
+#define NGX_HTTP_OFF_5XX   (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)
 
     ngx_string(ngx_http_error_494_page), /* 494, request header too large */
     ngx_string(ngx_http_error_495_page), /* 495, https certificate error */
@@ -342,7 +354,7 @@ static ngx_str_t ngx_http_error_pages[] 
     ngx_null_string,                     /* 506 */
     ngx_string(ngx_http_error_507_page)
 
-#define NGX_HTTP_LAST_LEVEL_500  508
+#define NGX_HTTP_LAST_5XX  508
 
 };
 
@@ -428,25 +440,22 @@ ngx_http_special_response_handler(ngx_ht
         err = 0;
 
     } else if (error >= NGX_HTTP_MOVED_PERMANENTLY
-               && error < NGX_HTTP_LAST_LEVEL_300)
+               && error < NGX_HTTP_LAST_3XX)
     {
         /* 3XX */
-        err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_LEVEL_200;
+        err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;
 
     } else if (error >= NGX_HTTP_BAD_REQUEST
-               && error < NGX_HTTP_LAST_LEVEL_400)
+               && error < NGX_HTTP_LAST_4XX)
     {
         /* 4XX */
-        err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_LEVEL_200
-                                           + NGX_HTTP_LEVEL_300;
+        err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_OFF_4XX;
 
     } else if (error >= NGX_HTTP_NGINX_CODES
-               && error < NGX_HTTP_LAST_LEVEL_500)
+               && error < NGX_HTTP_LAST_5XX)
     {
         /* 49X, 5XX */
-        err = error - NGX_HTTP_NGINX_CODES + NGX_HTTP_LEVEL_200
-                                           + NGX_HTTP_LEVEL_300
-                                           + NGX_HTTP_LEVEL_400;
+        err = error - NGX_HTTP_NGINX_CODES + NGX_HTTP_OFF_5XX;
         switch (error) {
             case NGX_HTTP_TO_HTTPS:
             case NGX_HTTPS_CERT_ERROR:
@@ -570,12 +579,11 @@ ngx_http_send_error_page(ngx_http_reques
         return NGX_ERROR;
     }
 
-    if (overwrite >= NGX_HTTP_MOVED_PERMANENTLY
-        && overwrite <= NGX_HTTP_SEE_OTHER)
+    if (overwrite != NGX_HTTP_MOVED_PERMANENTLY
+        && overwrite != NGX_HTTP_MOVED_TEMPORARILY
+        && overwrite != NGX_HTTP_SEE_OTHER
+        && overwrite != NGX_HTTP_TEMPORARY_REDIRECT)
     {
-        r->err_status = overwrite;
-
-    } else {
         r->err_status = NGX_HTTP_MOVED_TEMPORARILY;
     }
 
@@ -595,7 +603,7 @@ ngx_http_send_error_page(ngx_http_reques
 
     return ngx_http_send_special_response(r, clcf, r->err_status
                                                    - NGX_HTTP_MOVED_PERMANENTLY
-                                                   + NGX_HTTP_LEVEL_200);
+                                                   + NGX_HTTP_OFF_3XX);
 }
 
 
@@ -626,7 +634,7 @@ ngx_http_send_special_response(ngx_http_
         if (clcf->msie_padding
             && (r->headers_in.msie || r->headers_in.chrome)
             && r->http_version >= NGX_HTTP_VERSION_10
-            && err >= NGX_HTTP_LEVEL_300)
+            && err >= NGX_HTTP_OFF_4XX)
         {
             r->headers_out.content_length_n +=
                                          sizeof(ngx_http_msie_padding) - 1;
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -2293,7 +2293,7 @@ ngx_http_upstream_send_response(ngx_http
     }
 
     if (ngx_event_flags & NGX_USE_AIO_EVENT) {
-        /* the posted aio operation may currupt a shadow buffer */
+        /* the posted aio operation may corrupt a shadow buffer */
         p->single_buf = 1;
     }
 
--- a/src/mail/ngx_mail_pop3_handler.c
+++ b/src/mail/ngx_mail_pop3_handler.c
@@ -218,7 +218,7 @@ ngx_mail_pop3_auth_state(ngx_event_t *re
 
             break;
 
-        /* suppress warinings */
+        /* suppress warnings */
         case ngx_pop3_passwd:
             break;
 
--- a/src/os/unix/ngx_darwin_sendfile_chain.c
+++ b/src/os/unix/ngx_darwin_sendfile_chain.c
@@ -173,7 +173,7 @@ ngx_darwin_sendfile_chain(ngx_connection
 
         if (file && header.nelts == 0) {
 
-            /* create the tailer iovec and coalesce the neighbouring bufs */
+            /* create the trailer iovec and coalesce the neighbouring bufs */
 
             prev = NULL;
             iov = NULL;
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -78,8 +78,25 @@ typedef struct {
 
 #if (NGX_HAVE_OPENAT)
 #define NGX_FILE_NOFOLLOW        O_NOFOLLOW
+
+#if defined(O_DIRECTORY)
+#define NGX_FILE_DIRECTORY       O_DIRECTORY
+#else
+#define NGX_FILE_DIRECTORY       0
 #endif
 
+#if defined(O_SEARCH)
+#define NGX_FILE_SEARCH          O_SEARCH|NGX_FILE_DIRECTORY
+
+#elif defined(O_EXEC)
+#define NGX_FILE_SEARCH          O_EXEC|NGX_FILE_DIRECTORY
+
+#else
+#define NGX_FILE_SEARCH          O_RDONLY|NGX_FILE_DIRECTORY
+#endif
+
+#endif /* NGX_HAVE_OPENAT */
+
 #define NGX_FILE_DEFAULT_ACCESS  0644
 #define NGX_FILE_OWNER_ACCESS    0600
 
@@ -163,7 +180,7 @@ ngx_int_t ngx_set_file_time(u_char *name
 #define ngx_is_exec(sb)          (((sb)->st_mode & S_IXUSR) == S_IXUSR)
 #define ngx_file_access(sb)      ((sb)->st_mode & 0777)
 #define ngx_file_size(sb)        (sb)->st_size
-#define ngx_file_fs_size(sb)     ((sb)->st_blocks * 512)
+#define ngx_file_fs_size(sb)     ngx_max((sb)->st_size, (sb)->st_blocks * 512)
 #define ngx_file_mtime(sb)       (sb)->st_mtime
 #define ngx_file_uniq(sb)        (sb)->st_ino
 
@@ -259,7 +276,8 @@ ngx_de_info(u_char *name, ngx_dir_t *dir
 
 #define ngx_de_access(dir)       (((dir)->info.st_mode) & 0777)
 #define ngx_de_size(dir)         (dir)->info.st_size
-#define ngx_de_fs_size(dir)      ((dir)->info.st_blocks * 512)
+#define ngx_de_fs_size(dir)                                                  \
+    ngx_max((dir)->info.st_size, (dir)->info.st_blocks * 512)
 #define ngx_de_mtime(dir)        (dir)->info.st_mtime
 
 
@@ -340,6 +358,8 @@ size_t ngx_fs_bsize(u_char *name);
 
 #define ngx_file_at_info_n       "fstatat()"
 
+#define NGX_AT_FDCWD             (ngx_fd_t) AT_FDCWD
+
 #endif
 
 
--- a/src/os/unix/ngx_freebsd_sendfile_chain.c
+++ b/src/os/unix/ngx_freebsd_sendfile_chain.c
@@ -178,7 +178,7 @@ ngx_freebsd_sendfile_chain(ngx_connectio
 
         if (file) {
 
-            /* create the tailer iovec and coalesce the neighbouring bufs */
+            /* create the trailer iovec and coalesce the neighbouring bufs */
 
             prev = NULL;
             iov = NULL;
--- a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -250,6 +250,10 @@ ngx_master_process_cycle(ngx_cycle_t *cy
             ngx_start_worker_processes(cycle, ccf->worker_processes,
                                        NGX_PROCESS_JUST_RESPAWN);
             ngx_start_cache_manager_processes(cycle, 1);
+
+            /* allow new processes to start */
+            ngx_msleep(100);
+
             live = 1;
             ngx_signal_worker_processes(cycle,
                                         ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
--- a/src/os/unix/ngx_user.c
+++ b/src/os/unix/ngx_user.c
@@ -67,7 +67,7 @@ ngx_libc_crypt(ngx_pool_t *pool, u_char 
 
 #if (NGX_THREADS && NGX_NONREENTRANT_CRYPT)
 
-    /* crypt() is a time consuming funtion, so we only try to lock */
+    /* crypt() is a time consuming function, so we only try to lock */
 
     if (ngx_mutex_trylock(ngx_crypt_mutex) != NGX_OK) {
         return NGX_AGAIN;