changeset 662:e5fa0a4a7d27 NGINX_1_1_15

nginx 1.1.15 *) Feature: the "disable_symlinks" directive. *) Feature: the "proxy_cookie_domain" and "proxy_cookie_path" directives. *) Bugfix: nginx might log incorrect error "upstream prematurely closed connection" instead of correct "upstream sent too big header" one. Thanks to Feibo Li. *) Bugfix: nginx could not be built with the ngx_http_perl_module if the --with-openssl option was used. *) Bugfix: internal redirects to named locations were not limited. *) Bugfix: calling $r->flush() multiple times might cause errors in the ngx_http_gzip_filter_module. *) Bugfix: temporary files might be not removed if the "proxy_store" directive were used with SSI includes. *) Bugfix: in some cases non-cacheable variables (such as the $args variable) returned old empty cached value. *) Bugfix: a segmentation fault might occur in a worker process if too many SSI subrequests were issued simultaneously; the bug had appeared in 0.7.25.
author Igor Sysoev <http://sysoev.ru>
date Wed, 15 Feb 2012 00:00:00 +0400
parents b49c1751031c
children dd668cf20818
files CHANGES CHANGES.ru auto/lib/perl/make auto/unix src/core/nginx.h src/core/ngx_conf_file.c src/core/ngx_core.h src/core/ngx_open_file_cache.c src/core/ngx_open_file_cache.h src/core/ngx_parse.h src/core/ngx_resolver.c src/http/modules/ngx_http_flv_module.c src/http/modules/ngx_http_gzip_filter_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_log_module.c src/http/modules/ngx_http_mp4_module.c src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_range_filter_module.c src/http/modules/ngx_http_static_module.c src/http/modules/ngx_http_userid_filter_module.c src/http/modules/perl/Makefile.PL src/http/modules/perl/nginx.pm src/http/modules/perl/nginx.xs src/http/ngx_http_busy_lock.c 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_request.c src/http/ngx_http_request.h src/http/ngx_http_script.c src/http/ngx_http_upstream.c src/http/ngx_http_upstream.h src/http/ngx_http_variables.c src/mail/ngx_mail_core_module.c src/os/unix/ngx_errno.h src/os/unix/ngx_files.h src/os/unix/ngx_posix_init.c
diffstat 40 files changed, 1144 insertions(+), 250 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES	Mon Jan 30 00:00:00 2012 +0400
+++ b/CHANGES	Wed Feb 15 00:00:00 2012 +0400
@@ -1,4 +1,34 @@
 
+Changes with nginx 1.1.15                                        15 Feb 2012
+
+    *) Feature: the "disable_symlinks" directive.
+
+    *) Feature: the "proxy_cookie_domain" and "proxy_cookie_path"
+       directives.
+
+    *) Bugfix: nginx might log incorrect error "upstream prematurely closed
+       connection" instead of correct "upstream sent too big header" one.
+       Thanks to Feibo Li.
+
+    *) Bugfix: nginx could not be built with the ngx_http_perl_module if the
+       --with-openssl option was used.
+
+    *) Bugfix: internal redirects to named locations were not limited.
+
+    *) Bugfix: calling $r->flush() multiple times might cause errors in the
+       ngx_http_gzip_filter_module.
+
+    *) Bugfix: temporary files might be not removed if the "proxy_store"
+       directive were used with SSI includes.
+
+    *) Bugfix: in some cases non-cacheable variables (such as the $args
+       variable) returned old empty cached value.
+
+    *) Bugfix: a segmentation fault might occur in a worker process if too
+       many SSI subrequests were issued simultaneously; the bug had appeared
+       in 0.7.25.
+
+
 Changes with nginx 1.1.14                                        30 Jan 2012
 
     *) Feature: multiple "limit_req" limits may be used simultaneously.
--- a/CHANGES.ru	Mon Jan 30 00:00:00 2012 +0400
+++ b/CHANGES.ru	Wed Feb 15 00:00:00 2012 +0400
@@ -1,4 +1,34 @@
 
+Изменения в nginx 1.1.15                                          15.02.2012
+
+    *) Добавление: директива disable_symlinks.
+
+    *) Добавление: директивы proxy_cookie_domain и proxy_cookie_path.
+
+    *) Исправление: nginx мог некорректно сообщать об ошибке "upstream
+       prematurely closed connection" вместо "upstream sent too big header".
+       Спасибо Feibo Li.
+
+    *) Исправление: nginx не собирался с модулем ngx_http_perl_module, если
+       использовался параметр --with-openssl.
+
+    *) Исправление: количество внутренних перенаправлений в именованные
+       location'ы не ограничивалось.
+
+    *) Исправление: вызов $r->flush() несколько раз подряд мог приводить к
+       ошибкам в модуле ngx_http_gzip_filter_module.
+
+    *) Исправление: при использовании директивы proxy_store с
+       SSI-подзапросами временные файлы могли не удаляться.
+
+    *) Исправление: в некоторых случаях некэшируемые переменные (такие, как
+       $args) возвращали старое пустое закэшированное значение.
+
+    *) Исправление: в рабочем процессе мог произойти segmentation fault,
+       если одновременно создавалось слишком много SSI-подзапросов; ошибка
+       появилась в 0.7.25.
+
+
 Изменения в nginx 1.1.14                                          30.01.2012
 
     *) Добавление: теперь можно указать несколько ограничений limit_req
--- a/auto/lib/perl/make	Mon Jan 30 00:00:00 2012 +0400
+++ b/auto/lib/perl/make	Wed Feb 15 00:00:00 2012 +0400
@@ -28,6 +28,7 @@
 		&& NGX_PM_CFLAGS="\$(NGX_PM_CFLAGS) -g $NGX_CC_OPT"	\
 			NGX_PCRE=$PCRE					\
 			NGX_OBJS=$NGX_OBJS				\
+			NGX_OPENSSL=$OPENSSL				\
 		$NGX_PERL Makefile.PL					\
 			LIB=$NGX_PERL_MODULES				\
 			INSTALLSITEMAN3DIR=$NGX_PERL_MODULES_MAN
--- a/auto/unix	Mon Jan 30 00:00:00 2012 +0400
+++ b/auto/unix	Wed Feb 15 00:00:00 2012 +0400
@@ -721,3 +721,27 @@
 ngx_feature_libs=
 ngx_feature_test="struct dirent  dir; dir.d_type = DT_REG"
 . auto/feature
+
+
+ngx_feature="sysconf(_SC_NPROCESSORS_ONLN)"
+ngx_feature_name="NGX_HAVE_SC_NPROCESSORS_ONLN"
+ngx_feature_run=no
+ngx_feature_incs=
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="sysconf(_SC_NPROCESSORS_ONLN)"
+. auto/feature
+
+
+ngx_feature="openat(), fstatat()"
+ngx_feature_name="NGX_HAVE_OPENAT"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/types.h>
+                  #include <sys/stat.h>
+                  #include <fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct stat sb;
+                  openat(AT_FDCWD, \".\", O_RDONLY|O_NOFOLLOW);
+                  fstatat(AT_FDCWD, \".\", &sb, AT_SYMLINK_NOFOLLOW);"
+. auto/feature
--- a/src/core/nginx.h	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/core/nginx.h	Wed Feb 15 00:00:00 2012 +0400
@@ -9,8 +9,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version      1001014
-#define NGINX_VERSION      "1.1.14"
+#define nginx_version      1001015
+#define NGINX_VERSION      "1.1.15"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_conf_file.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/core/ngx_conf_file.c	Wed Feb 15 00:00:00 2012 +0400
@@ -1295,10 +1295,6 @@
         return "invalid value";
     }
 
-    if (*msp == (ngx_msec_t) NGX_PARSE_LARGE_TIME) {
-        return "value must be less than 597 hours";
-    }
-
     if (cmd->post) {
         post = cmd->post;
         return post->post_handler(cf, post, msp);
@@ -1326,14 +1322,10 @@
     value = cf->args->elts;
 
     *sp = ngx_parse_time(&value[1], 1);
-    if (*sp == NGX_ERROR) {
+    if (*sp == (time_t) NGX_ERROR) {
         return "invalid value";
     }
 
-    if (*sp == NGX_PARSE_LARGE_TIME) {
-        return "value must be less than 68 years";
-    }
-
     if (cmd->post) {
         post = cmd->post;
         return post->post_handler(cf, post, sp);
--- a/src/core/ngx_core.h	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/core/ngx_core.h	Wed Feb 15 00:00:00 2012 +0400
@@ -91,5 +91,10 @@
 
 void ngx_cpuinfo(void);
 
+#if (NGX_HAVE_OPENAT)
+#define NGX_DISABLE_SYMLINKS_OFF        0
+#define NGX_DISABLE_SYMLINKS_ON         1
+#define NGX_DISABLE_SYMLINKS_NOTOWNER   2
+#endif
 
 #endif /* _NGX_CORE_H_INCLUDED_ */
--- a/src/core/ngx_open_file_cache.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/core/ngx_open_file_cache.c	Wed Feb 15 00:00:00 2012 +0400
@@ -22,8 +22,17 @@
 
 
 static void ngx_open_file_cache_cleanup(void *data);
-static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of,
-    ngx_log_t *log);
+#if (NGX_HAVE_OPENAT)
+static ngx_fd_t ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name,
+    ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log);
+#endif
+static ngx_fd_t ngx_open_file_wrapper(ngx_str_t *name,
+    ngx_open_file_info_t *of, ngx_int_t mode, ngx_int_t create,
+    ngx_int_t access, ngx_log_t *log);
+static ngx_int_t ngx_file_info_wrapper(ngx_str_t *name,
+    ngx_open_file_info_t *of, ngx_file_info_t *fi, ngx_log_t *log);
+static ngx_int_t ngx_open_and_stat_file(ngx_str_t *name,
+    ngx_open_file_info_t *of, ngx_log_t *log);
 static void ngx_open_file_add_event(ngx_open_file_cache_t *cache,
     ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log);
 static void ngx_open_file_cleanup(void *data);
@@ -147,9 +156,9 @@
 
         if (of->test_only) {
 
-            if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
-                of->err = ngx_errno;
-                of->failed = ngx_file_info_n;
+            if (ngx_file_info_wrapper(name, of, &fi, pool->log)
+                == NGX_FILE_ERROR)
+            {
                 return NGX_ERROR;
             }
 
@@ -170,7 +179,7 @@
             return NGX_ERROR;
         }
 
-        rc = ngx_open_and_stat_file(name->data, of, pool->log);
+        rc = ngx_open_and_stat_file(name, of, pool->log);
 
         if (rc == NGX_OK && !of->is_dir) {
             cln->handler = ngx_pool_cleanup_file;
@@ -205,7 +214,7 @@
 
             /* file was not used often enough to keep open */
 
-            rc = ngx_open_and_stat_file(name->data, of, pool->log);
+            rc = ngx_open_and_stat_file(name, of, pool->log);
 
             if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
                 goto failed;
@@ -217,7 +226,11 @@
         if (file->use_event
             || (file->event == NULL
                 && (of->uniq == 0 || of->uniq == file->uniq)
-                && now - file->created < of->valid))
+                && now - file->created < of->valid
+#if (NGX_HAVE_OPENAT)
+                && of->disable_symlinks == file->disable_symlinks
+#endif
+            ))
         {
             if (file->err == 0) {
 
@@ -239,7 +252,12 @@
 
             } else {
                 of->err = file->err;
+#if (NGX_HAVE_OPENAT)
+                of->failed = file->disable_symlinks ? ngx_openat_file_n
+                                                    : ngx_open_file_n;
+#else
                 of->failed = ngx_open_file_n;
+#endif
             }
 
             goto found;
@@ -263,7 +281,7 @@
         of->fd = file->fd;
         of->uniq = file->uniq;
 
-        rc = ngx_open_and_stat_file(name->data, of, pool->log);
+        rc = ngx_open_and_stat_file(name, of, pool->log);
 
         if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
             goto failed;
@@ -311,8 +329,7 @@
 
             if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
                 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
-                              ngx_close_file_n " \"%s\" failed",
-                              name->data);
+                              ngx_close_file_n " \"%V\" failed", name);
             }
 
             goto add_event;
@@ -329,7 +346,7 @@
 
     /* not found */
 
-    rc = ngx_open_and_stat_file(name->data, of, pool->log);
+    rc = ngx_open_and_stat_file(name, of, pool->log);
 
     if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
         goto failed;
@@ -376,6 +393,9 @@
 
     file->fd = of->fd;
     file->err = of->err;
+#if (NGX_HAVE_OPENAT)
+    file->disable_symlinks = of->disable_symlinks;
+#endif
 
     if (of->err == 0) {
         file->uniq = of->uniq;
@@ -452,7 +472,7 @@
     if (of->fd != NGX_INVALID_FILE) {
         if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
             ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
-                          ngx_close_file_n " \"%s\" failed", name->data);
+                          ngx_close_file_n " \"%V\" failed", name);
         }
     }
 
@@ -460,17 +480,297 @@
 }
 
 
+#if (NGX_HAVE_OPENAT)
+
+static ngx_fd_t
+ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name,
+    ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log)
+{
+    ngx_fd_t         fd;
+    ngx_err_t        err;
+    ngx_file_info_t  fi, atfi;
+
+    /*
+     * To allow symlinks with the same owner, use openat() (followed
+     * by fstat()) and fstatat(AT_SYMLINK_NOFOLLOW), and then compare
+     * uids between fstat() and fstatat().
+     *
+     * As there is a race between openat() and fstatat() we don't
+     * know if openat() in fact opened symlink or not.  Therefore,
+     * we have to compare uids even if fstatat() reports the opened
+     * component isn't a symlink (as we don't know whether it was
+     * symlink during openat() or not).
+     */
+
+    fd = ngx_openat_file(at_fd, name, mode, create, access);
+
+    if (fd == NGX_FILE_ERROR) {
+        return NGX_FILE_ERROR;
+    }
+
+    if (ngx_file_at_info(at_fd, name, &atfi, AT_SYMLINK_NOFOLLOW)
+        == NGX_FILE_ERROR)
+    {
+        err = ngx_errno;
+        goto failed;
+    }
+
+    if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
+        err = ngx_errno;
+        goto failed;
+    }
+
+    if (fi.st_uid != atfi.st_uid) {
+        err = NGX_ELOOP;
+        goto failed;
+    }
+
+    return fd;
+
+failed:
+
+    if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      ngx_close_file_n " \"%V\" failed", name);
+    }
+
+    ngx_set_errno(err);
+
+    return NGX_INVALID_FILE;
+}
+
+#endif
+
+
+static ngx_fd_t
+ngx_open_file_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,
+    ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log)
+{
+    ngx_fd_t  fd;
+
+#if !(NGX_HAVE_OPENAT)
+
+    fd = ngx_open_file(name->data, mode, create, access);
+
+    if (fd == NGX_INVALID_FILE) {
+        of->err = ngx_errno;
+        of->failed = ngx_open_file_n;
+        return NGX_INVALID_FILE;
+    }
+
+    return fd;
+
+#else
+
+    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);
+
+        if (fd == NGX_INVALID_FILE) {
+            of->err = ngx_errno;
+            of->failed = ngx_open_file_n;
+            return NGX_INVALID_FILE;
+        }
+
+        return fd;
+    }
+
+    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 (at_fd == NGX_FILE_ERROR) {
+            of->err = ngx_errno;
+            of->failed = ngx_openat_file_n;
+            return NGX_FILE_ERROR;
+        }
+
+        at_name.len = 1;
+        p++;
+    }
+
+    for ( ;; ) {
+        cp = ngx_strlchr(p, end, '/');
+        if (cp == NULL) {
+            break;
+        }
+
+        if (cp == p) {
+            p++;
+            continue;
+        }
+
+        *cp = '\0';
+
+        if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) {
+            fd = ngx_openat_file_owner(at_fd, p,
+                                       NGX_FILE_RDONLY|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_OPEN, 0);
+        }
+
+        *cp = '/';
+
+        if (fd == NGX_INVALID_FILE) {
+            of->err = ngx_errno;
+            of->failed = ngx_openat_file_n;
+            goto failed;
+        }
+
+        if (at_fd != 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);
+        }
+
+        p = cp + 1;
+        at_fd = fd;
+        at_name.len = cp - at_name.data;
+    }
+
+    if (p == end && at_fd != AT_FDCWD) {
+
+        /*
+         * If pathname ends with a trailing slash, check if last path
+         * component is a directory; 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.
+         */
+
+        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;
+    }
+
+    if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) {
+        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);
+    }
+
+    if (fd == NGX_INVALID_FILE) {
+        of->err = ngx_errno;
+        of->failed = ngx_openat_file_n;
+    }
+
+failed:
+
+    if (at_fd != 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);
+    }
+
+    return fd;
+#endif
+}
+
+
 static ngx_int_t
-ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log)
+ngx_file_info_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,
+    ngx_file_info_t *fi, ngx_log_t *log)
+{
+    ngx_int_t  rc;
+
+#if !(NGX_HAVE_OPENAT)
+
+    rc = ngx_file_info(name->data, fi);
+
+    if (rc == NGX_FILE_ERROR) {
+        of->err = ngx_errno;
+        of->failed = ngx_file_info_n;
+        return NGX_FILE_ERROR;
+    }
+
+    return rc;
+
+#else
+
+    ngx_fd_t  fd;
+
+    if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {
+
+        rc = ngx_file_info(name->data, fi);
+
+        if (rc == NGX_FILE_ERROR) {
+            of->err = ngx_errno;
+            of->failed = ngx_file_info_n;
+            return NGX_FILE_ERROR;
+        }
+
+        return rc;
+    }
+
+    fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
+                               NGX_FILE_OPEN, 0, log);
+
+    if (fd == NGX_INVALID_FILE) {
+        return NGX_FILE_ERROR;
+    }
+
+    rc = ngx_fd_info(fd, fi);
+
+    if (rc == NGX_FILE_ERROR) {
+        of->err = ngx_errno;
+        of->failed = ngx_fd_info_n;
+    }
+
+    if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      ngx_close_file_n " \"%V\" failed", name);
+    }
+
+    return rc;
+#endif
+}
+
+
+static ngx_int_t
+ngx_open_and_stat_file(ngx_str_t *name, ngx_open_file_info_t *of,
+    ngx_log_t *log)
 {
     ngx_fd_t         fd;
     ngx_file_info_t  fi;
 
     if (of->fd != NGX_INVALID_FILE) {
 
-        if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {
-            of->failed = ngx_file_info_n;
-            goto failed;
+        if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {
+            of->fd = NGX_INVALID_FILE;
+            return NGX_ERROR;
         }
 
         if (of->uniq == ngx_file_uniq(&fi)) {
@@ -479,9 +779,9 @@
 
     } else if (of->test_dir) {
 
-        if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {
-            of->failed = ngx_file_info_n;
-            goto failed;
+        if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {
+            of->fd = NGX_INVALID_FILE;
+            return NGX_ERROR;
         }
 
         if (ngx_is_dir(&fi)) {
@@ -496,26 +796,27 @@
          * This flag has no effect on a regular files.
          */
 
-        fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
-                           NGX_FILE_OPEN, 0);
+        fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
+                                   NGX_FILE_OPEN, 0, log);
 
     } else {
-        fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN,
-                           NGX_FILE_DEFAULT_ACCESS);
+        fd = ngx_open_file_wrapper(name, of, NGX_FILE_APPEND,
+                                   NGX_FILE_CREATE_OR_OPEN,
+                                   NGX_FILE_DEFAULT_ACCESS, log);
     }
 
     if (fd == NGX_INVALID_FILE) {
-        of->failed = ngx_open_file_n;
-        goto failed;
+        of->fd = NGX_INVALID_FILE;
+        return NGX_ERROR;
     }
 
     if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
         ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
-                      ngx_fd_info_n " \"%s\" failed", name);
+                      ngx_fd_info_n " \"%V\" failed", name);
 
         if (ngx_close_file(fd) == NGX_FILE_ERROR) {
             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                          ngx_close_file_n " \"%s\" failed", name);
+                          ngx_close_file_n " \"%V\" failed", name);
         }
 
         of->fd = NGX_INVALID_FILE;
@@ -526,7 +827,7 @@
     if (ngx_is_dir(&fi)) {
         if (ngx_close_file(fd) == NGX_FILE_ERROR) {
             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                          ngx_close_file_n " \"%s\" failed", name);
+                          ngx_close_file_n " \"%V\" failed", name);
         }
 
         of->fd = NGX_INVALID_FILE;
@@ -537,14 +838,14 @@
         if (of->read_ahead && ngx_file_size(&fi) > NGX_MIN_READ_AHEAD) {
             if (ngx_read_ahead(fd, of->read_ahead) == NGX_ERROR) {
                 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                              ngx_read_ahead_n " \"%s\" failed", name);
+                              ngx_read_ahead_n " \"%V\" failed", name);
             }
         }
 
         if (of->directio <= ngx_file_size(&fi)) {
             if (ngx_directio_on(fd) == NGX_FILE_ERROR) {
                 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                              ngx_directio_on_n " \"%s\" failed", name);
+                              ngx_directio_on_n " \"%V\" failed", name);
 
             } else {
                 of->is_directio = 1;
@@ -564,13 +865,6 @@
     of->is_exec = ngx_is_exec(&fi);
 
     return NGX_OK;
-
-failed:
-
-    of->fd = NGX_INVALID_FILE;
-    of->err = ngx_errno;
-
-    return NGX_ERROR;
 }
 
 
--- a/src/core/ngx_open_file_cache.h	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/core/ngx_open_file_cache.h	Wed Feb 15 00:00:00 2012 +0400
@@ -32,6 +32,10 @@
 
     ngx_uint_t               min_uses;
 
+#if (NGX_HAVE_OPENAT)
+    unsigned                 disable_symlinks:2;
+#endif
+
     unsigned                 test_dir:1;
     unsigned                 test_only:1;
     unsigned                 log:1;
@@ -64,6 +68,10 @@
 
     uint32_t                 uses;
 
+#if (NGX_HAVE_OPENAT)
+    unsigned                 disable_symlinks:2;
+#endif
+
     unsigned                 count:24;
     unsigned                 close:1;
     unsigned                 use_event:1;
--- a/src/core/ngx_parse.h	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/core/ngx_parse.h	Wed Feb 15 00:00:00 2012 +0400
@@ -13,9 +13,6 @@
 #include <ngx_core.h>
 
 
-#define NGX_PARSE_LARGE_TIME  -2
-
-
 ssize_t ngx_parse_size(ngx_str_t *line);
 off_t ngx_parse_offset(ngx_str_t *line);
 ngx_int_t ngx_parse_time(ngx_str_t *line, ngx_uint_t is_sec);
--- a/src/core/ngx_resolver.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/core/ngx_resolver.c	Wed Feb 15 00:00:00 2012 +0400
@@ -160,7 +160,7 @@
 
             r->valid = ngx_parse_time(&s, 1);
 
-            if (r->valid == NGX_ERROR) {
+            if (r->valid == (time_t) NGX_ERROR) {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                    "invalid parameter: %V", &names[i]);
                 return NULL;
--- a/src/http/modules/ngx_http_flv_module.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/ngx_http_flv_module.c	Wed Feb 15 00:00:00 2012 +0400
@@ -109,6 +109,9 @@
     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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
         != NGX_OK)
@@ -127,6 +130,10 @@
             break;
 
         case NGX_EACCES:
+#if (NGX_HAVE_OPENAT)
+        case NGX_EMLINK:
+        case NGX_ELOOP:
+#endif
 
             level = NGX_LOG_ERR;
             rc = NGX_HTTP_FORBIDDEN;
--- a/src/http/modules/ngx_http_gzip_filter_module.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/ngx_http_gzip_filter_module.c	Wed Feb 15 00:00:00 2012 +0400
@@ -759,6 +759,7 @@
 ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
 {
     int                    rc;
+    ngx_buf_t             *b;
     ngx_chain_t           *cl;
     ngx_http_gzip_conf_t  *conf;
 
@@ -770,7 +771,7 @@
 
     rc = deflate(&ctx->zstream, ctx->flush);
 
-    if (rc != Z_OK && rc != Z_STREAM_END) {
+    if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {
         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                       "deflate() failed: %d, %d", ctx->flush, rc);
         return NGX_ERROR;
@@ -819,8 +820,6 @@
 
     if (ctx->flush == Z_SYNC_FLUSH) {
 
-        ctx->zstream.avail_out = 0;
-        ctx->out_buf->flush = 1;
         ctx->flush = Z_NO_FLUSH;
 
         cl = ngx_alloc_chain_link(r->pool);
@@ -828,7 +827,22 @@
             return NGX_ERROR;
         }
 
-        cl->buf = ctx->out_buf;
+        b = ctx->out_buf;
+
+        if (ngx_buf_size(b) == 0) {
+
+            b = ngx_calloc_buf(ctx->request->pool);
+            if (b == NULL) {
+                return NGX_ERROR;
+            }
+
+        } else {
+            ctx->zstream.avail_out = 0;
+        }
+
+        b->flush = 1;
+
+        cl->buf = b;
         cl->next = NULL;
         *ctx->last_out = cl;
         ctx->last_out = &cl->next;
--- a/src/http/modules/ngx_http_gzip_static_module.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/ngx_http_gzip_static_module.c	Wed Feb 15 00:00:00 2012 +0400
@@ -129,6 +129,9 @@
     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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
         != NGX_OK)
@@ -145,6 +148,10 @@
             return NGX_DECLINED;
 
         case NGX_EACCES:
+#if (NGX_HAVE_OPENAT)
+        case NGX_EMLINK:
+        case NGX_ELOOP:
+#endif
 
             level = NGX_LOG_ERR;
             break;
--- a/src/http/modules/ngx_http_headers_filter_module.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/ngx_http_headers_filter_module.c	Wed Feb 15 00:00:00 2012 +0400
@@ -530,7 +530,7 @@
 
     hcf->expires_time = ngx_parse_time(&value[n], 1);
 
-    if (hcf->expires_time == NGX_ERROR) {
+    if (hcf->expires_time == (time_t) NGX_ERROR) {
         return "invalid value";
     }
 
@@ -540,10 +540,6 @@
         return "daily time value must be less than 24 hours";
     }
 
-    if (hcf->expires_time == NGX_PARSE_LARGE_TIME) {
-        return "value must be less than 68 years";
-    }
-
     if (minus) {
         hcf->expires_time = - hcf->expires_time;
     }
--- a/src/http/modules/ngx_http_index_module.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/ngx_http_index_module.c	Wed Feb 15 00:00:00 2012 +0400
@@ -209,6 +209,9 @@
         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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
             != NGX_OK)
@@ -220,6 +223,14 @@
                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
             }
 
+#if (NGX_HAVE_OPENAT)
+            if (of.err == NGX_EMLINK
+                || of.err == NGX_ELOOP)
+            {
+                return NGX_HTTP_FORBIDDEN;
+            }
+#endif
+
             if (of.err == NGX_ENOTDIR
                 || of.err == NGX_ENAMETOOLONG
                 || of.err == NGX_EACCES)
@@ -296,12 +307,23 @@
     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_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool)
         != NGX_OK)
     {
         if (of.err) {
 
+#if (NGX_HAVE_OPENAT)
+            if (of.err == NGX_EMLINK
+                || of.err == NGX_ELOOP)
+            {
+                return NGX_HTTP_FORBIDDEN;
+            }
+#endif
+
             if (of.err == NGX_ENOENT) {
                 *last = c;
                 return ngx_http_index_error(r, clcf, dir.data, NGX_ENOENT);
--- a/src/http/modules/ngx_http_limit_conn_module.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/ngx_http_limit_conn_module.c	Wed Feb 15 00:00:00 2012 +0400
@@ -159,8 +159,6 @@
         return NGX_DECLINED;
     }
 
-    r->main->limit_conn_set = 1;
-
     lccf = ngx_http_get_module_loc_conf(r, ngx_http_limit_conn_module);
     limits = lccf->limits.elts;
 
@@ -187,6 +185,8 @@
             continue;
         }
 
+        r->main->limit_conn_set = 1;
+
         hash = ngx_crc32_short(vv->data, len);
 
         shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr;
--- a/src/http/modules/ngx_http_log_module.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/ngx_http_log_module.c	Wed Feb 15 00:00:00 2012 +0400
@@ -373,6 +373,8 @@
     ngx_http_log_loc_conf_t   *llcf;
     ngx_http_core_loc_conf_t  *clcf;
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
     if (!r->root_tested) {
 
         /* test root directory existance */
@@ -384,8 +386,6 @@
 
         path.data[root] = '\0';
 
-        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
         ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
         of.valid = clcf->open_file_cache_valid;
@@ -394,6 +394,9 @@
         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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
             != NGX_OK)
@@ -441,6 +444,9 @@
     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_open_cached_file(llcf->open_file_cache, &log, &of, r->pool)
         != NGX_OK)
@@ -1249,7 +1255,7 @@
             s.data = value[i].data + 9;
 
             inactive = ngx_parse_time(&s, 1);
-            if (inactive < 0) {
+            if (inactive == (time_t) NGX_ERROR) {
                 goto failed;
             }
 
@@ -1272,7 +1278,7 @@
             s.data = value[i].data + 6;
 
             valid = ngx_parse_time(&s, 1);
-            if (valid < 0) {
+            if (valid == (time_t) NGX_ERROR) {
                 goto failed;
             }
 
--- a/src/http/modules/ngx_http_mp4_module.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/ngx_http_mp4_module.c	Wed Feb 15 00:00:00 2012 +0400
@@ -440,6 +440,9 @@
     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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
         != NGX_OK)
@@ -458,6 +461,10 @@
             break;
 
         case NGX_EACCES:
+#if (NGX_HAVE_OPENAT)
+        case NGX_EMLINK:
+        case NGX_ELOOP:
+#endif
 
             level = NGX_LOG_ERR;
             rc = NGX_HTTP_FORBIDDEN;
--- a/src/http/modules/ngx_http_proxy_module.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/ngx_http_proxy_module.c	Wed Feb 15 00:00:00 2012 +0400
@@ -10,20 +10,21 @@
 #include <ngx_http.h>
 
 
-typedef struct ngx_http_proxy_redirect_s  ngx_http_proxy_redirect_t;
-
-typedef ngx_int_t (*ngx_http_proxy_redirect_pt)(ngx_http_request_t *r,
-    ngx_table_elt_t *h, size_t prefix, ngx_http_proxy_redirect_t *pr);
-
-struct ngx_http_proxy_redirect_s {
-    ngx_http_proxy_redirect_pt     handler;
+typedef struct ngx_http_proxy_rewrite_s  ngx_http_proxy_rewrite_t;
+
+typedef ngx_int_t (*ngx_http_proxy_rewrite_pt)(ngx_http_request_t *r,
+    ngx_table_elt_t *h, size_t prefix, size_t len,
+    ngx_http_proxy_rewrite_t *pr);
+
+struct ngx_http_proxy_rewrite_s {
+    ngx_http_proxy_rewrite_pt      handler;
 
     union {
         ngx_http_complex_value_t   complex;
 #if (NGX_PCRE)
         ngx_http_regex_t          *regex;
 #endif
-    } redirect;
+    } pattern;
 
     ngx_http_complex_value_t       replacement;
 };
@@ -54,6 +55,8 @@
     ngx_array_t                   *proxy_values;
 
     ngx_array_t                   *redirects;
+    ngx_array_t                   *cookie_domains;
+    ngx_array_t                   *cookie_paths;
 
     ngx_str_t                      body_source;
 
@@ -123,6 +126,12 @@
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r,
     ngx_table_elt_t *h, size_t prefix);
+static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r,
+    ngx_table_elt_t *h);
+static ngx_int_t ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r,
+    ngx_table_elt_t *h, u_char *value, ngx_array_t *rewrites);
+static ngx_int_t ngx_http_proxy_rewrite(ngx_http_request_t *r,
+    ngx_table_elt_t *h, size_t prefix, size_t len, ngx_str_t *replacement);
 
 static ngx_int_t ngx_http_proxy_add_variables(ngx_conf_t *cf);
 static void *ngx_http_proxy_create_loc_conf(ngx_conf_t *cf);
@@ -135,6 +144,10 @@
     void *conf);
 static char *ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static char *ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 #if (NGX_HTTP_CACHE)
@@ -146,6 +159,9 @@
 
 static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data);
 
+static ngx_int_t ngx_http_proxy_rewrite_regex(ngx_conf_t *cf,
+    ngx_http_proxy_rewrite_t *pr, ngx_str_t *regex, ngx_uint_t caseless);
+
 #if (NGX_HTTP_SSL)
 static ngx_int_t ngx_http_proxy_set_ssl(ngx_conf_t *cf,
     ngx_http_proxy_loc_conf_t *plcf);
@@ -198,6 +214,20 @@
       0,
       NULL },
 
+    { ngx_string("proxy_cookie_domain"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
+      ngx_http_proxy_cookie_domain,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("proxy_cookie_path"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
+      ngx_http_proxy_cookie_path,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("proxy_store"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_http_proxy_store,
@@ -649,6 +679,10 @@
         u->rewrite_redirect = ngx_http_proxy_rewrite_redirect;
     }
 
+    if (plcf->cookie_domains || plcf->cookie_paths) {
+        u->rewrite_cookie = ngx_http_proxy_rewrite_cookie;
+    }
+
     u->buffering = plcf->upstream.buffering;
 
     u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
@@ -2272,10 +2306,11 @@
 ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h,
     size_t prefix)
 {
+    size_t                      len;
     ngx_int_t                   rc;
     ngx_uint_t                  i;
+    ngx_http_proxy_rewrite_t   *pr;
     ngx_http_proxy_loc_conf_t  *plcf;
-    ngx_http_proxy_redirect_t  *pr;
 
     plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
 
@@ -2285,8 +2320,95 @@
         return NGX_DECLINED;
     }
 
+    len = h->value.len - prefix;
+
     for (i = 0; i < plcf->redirects->nelts; i++) {
-        rc = pr[i].handler(r, h, prefix, &pr[i]);
+        rc = pr[i].handler(r, h, prefix, len, &pr[i]);
+
+        if (rc != NGX_DECLINED) {
+            return rc;
+        }
+    }
+
+    return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h)
+{
+    size_t                      prefix;
+    u_char                     *p;
+    ngx_int_t                   rc, rv;
+    ngx_http_proxy_loc_conf_t  *plcf;
+
+    p = (u_char *) ngx_strchr(h->value.data, ';');
+    if (p == NULL) {
+        return NGX_DECLINED;
+    }
+
+    prefix = p + 1 - h->value.data;
+
+    rv = NGX_DECLINED;
+
+    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
+
+    if (plcf->cookie_domains) {
+        p = ngx_strcasestrn(h->value.data + prefix, "domain=", 7 - 1);
+
+        if (p) {
+            rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 7,
+                                                     plcf->cookie_domains);
+            if (rc == NGX_ERROR) {
+                return NGX_ERROR;
+            }
+
+            if (rc != NGX_DECLINED) {
+                rv = rc;
+            }
+        }
+    }
+
+    if (plcf->cookie_paths) {
+        p = ngx_strcasestrn(h->value.data + prefix, "path=", 5 - 1);
+
+        if (p) {
+            rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 5,
+                                                     plcf->cookie_paths);
+            if (rc == NGX_ERROR) {
+                return NGX_ERROR;
+            }
+
+            if (rc != NGX_DECLINED) {
+                rv = rc;
+            }
+        }
+    }
+
+    return rv;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_table_elt_t *h,
+    u_char *value, ngx_array_t *rewrites)
+{
+    size_t                     len, prefix;
+    u_char                    *p;
+    ngx_int_t                  rc;
+    ngx_uint_t                 i;
+    ngx_http_proxy_rewrite_t  *pr;
+
+    prefix = value - h->value.data;
+
+    p = (u_char *) ngx_strchr(value, ';');
+
+    len = p ? (size_t) (p - value) : (h->value.len - prefix);
+
+    pr = rewrites->elts;
+
+    for (i = 0; i < rewrites->nelts; i++) {
+        rc = pr[i].handler(r, h, prefix, len, &pr[i]);
 
         if (rc != NGX_DECLINED) {
             return rc;
@@ -2298,20 +2420,18 @@
 
 
 static ngx_int_t
-ngx_http_proxy_rewrite_redirect_complex(ngx_http_request_t *r,
-    ngx_table_elt_t *h, size_t prefix, ngx_http_proxy_redirect_t *pr)
+ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r,
+    ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)
 {
-    size_t      len;
-    u_char     *data, *p;
-    ngx_str_t   redirect, replacement;
-
-    if (ngx_http_complex_value(r, &pr->redirect.complex, &redirect) != NGX_OK) {
+    ngx_str_t  pattern, replacement;
+
+    if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) {
         return NGX_ERROR;
     }
 
-    if (redirect.len > h->value.len - prefix
-        || ngx_rstrncmp(h->value.data + prefix, redirect.data,
-                        redirect.len) != 0)
+    if (pattern.len > len
+        || ngx_rstrncmp(h->value.data + prefix, pattern.data,
+                        pattern.len) != 0)
     {
         return NGX_DECLINED;
     }
@@ -2320,43 +2440,22 @@
         return NGX_ERROR;
     }
 
-    len = replacement.len + h->value.len - redirect.len;
-
-    data = ngx_pnalloc(r->pool, len);
-    if (data == NULL) {
-        return NGX_ERROR;
-    }
-
-    p = ngx_copy(data, h->value.data, prefix);
-
-    if (replacement.len) {
-        p = ngx_copy(p, replacement.data, replacement.len);
-    }
-
-    ngx_memcpy(p, h->value.data + prefix + redirect.len,
-               h->value.len - redirect.len - prefix);
-
-    h->value.len = len;
-    h->value.data = data;
-
-    return NGX_OK;
+    return ngx_http_proxy_rewrite(r, h, prefix, pattern.len, &replacement);
 }
 
 
 #if (NGX_PCRE)
 
 static ngx_int_t
-ngx_http_proxy_rewrite_redirect_regex(ngx_http_request_t *r, ngx_table_elt_t *h,
-    size_t prefix, ngx_http_proxy_redirect_t *pr)
+ngx_http_proxy_rewrite_regex_handler(ngx_http_request_t *r, ngx_table_elt_t *h,
+    size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)
 {
-    size_t      len;
-    u_char     *data;
-    ngx_str_t   redirect, replacement;
-
-    redirect.len = h->value.len - prefix;
-    redirect.data = h->value.data + prefix;
-
-    if (ngx_http_regex_exec(r, pr->redirect.regex, &redirect) != NGX_OK) {
+    ngx_str_t  pattern, replacement;
+
+    pattern.len = len;
+    pattern.data = h->value.data + prefix;
+
+    if (ngx_http_regex_exec(r, pr->pattern.regex, &pattern) != NGX_OK) {
         return NGX_DECLINED;
     }
 
@@ -2364,29 +2463,85 @@
         return NGX_ERROR;
     }
 
-    if (!prefix) {
+    if (prefix == 0 && h->value.len == len) {
         h->value = replacement;
         return NGX_OK;
     }
 
-    len = prefix + replacement.len;
-
-    data = ngx_pnalloc(r->pool, len);
-    if (data == NULL) {
+    return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement);
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r,
+    ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)
+{
+    u_char     *p;
+    ngx_str_t   pattern, replacement;
+
+    if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    p = h->value.data + prefix;
+
+    if (p[0] == '.') {
+        p++;
+        prefix++;
+        len--;
+    }
+
+    if (pattern.len != len || ngx_rstrncasecmp(pattern.data, p, len) != 0) {
+        return NGX_DECLINED;
+    }
+
+    if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {
         return NGX_ERROR;
     }
 
-    ngx_memcpy(data, h->value.data, prefix);
-    ngx_memcpy(data + prefix, replacement.data, replacement.len);
-
-    h->value.len = len;
-    h->value.data = data;
+    return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement);
+}
+
+
+static ngx_int_t
+ngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix,
+    size_t len, ngx_str_t *replacement)
+{
+    u_char  *p, *data;
+    size_t   new_len;
+
+    new_len = replacement->len + h->value.len - len;
+
+    if (replacement->len > len) {
+
+        data = ngx_pnalloc(r->pool, new_len);
+        if (data == NULL) {
+            return NGX_ERROR;
+        }
+
+        p = ngx_copy(data, h->value.data, prefix);
+        p = ngx_copy(p, replacement->data, replacement->len);
+
+        ngx_memcpy(p, h->value.data + prefix + len,
+                   h->value.len - len - prefix);
+
+        h->value.data = data;
+
+    } else {
+        p = ngx_copy(h->value.data + prefix, replacement->data,
+                     replacement->len);
+
+        ngx_memmove(p, h->value.data + prefix + len,
+                    h->value.len - len - prefix);
+    }
+
+    h->value.len = new_len;
 
     return NGX_OK;
 }
 
-#endif
-
 
 static ngx_int_t
 ngx_http_proxy_add_variables(ngx_conf_t *cf)
@@ -2486,6 +2641,9 @@
     conf->redirect = NGX_CONF_UNSET;
     conf->upstream.change_buffering = 1;
 
+    conf->cookie_domains = NGX_CONF_UNSET_PTR;
+    conf->cookie_paths = NGX_CONF_UNSET_PTR;
+
     conf->http_version = NGX_CONF_UNSET_UINT;
 
     conf->headers_hash_max_size = NGX_CONF_UNSET_UINT;
@@ -2507,7 +2665,7 @@
     size_t                      size;
     ngx_hash_init_t             hash;
     ngx_http_core_loc_conf_t   *clcf;
-    ngx_http_proxy_redirect_t  *pr;
+    ngx_http_proxy_rewrite_t   *pr;
     ngx_http_script_compile_t   sc;
 
     if (conf->upstream.store != 0) {
@@ -2760,7 +2918,7 @@
         if (conf->redirects == NULL && conf->url.data) {
 
             conf->redirects = ngx_array_create(cf->pool, 1,
-                                            sizeof(ngx_http_proxy_redirect_t));
+                                             sizeof(ngx_http_proxy_rewrite_t));
             if (conf->redirects == NULL) {
                 return NGX_CONF_ERROR;
             }
@@ -2770,27 +2928,27 @@
                 return NGX_CONF_ERROR;
             }
 
-            ngx_memzero(&pr->redirect.complex,
+            ngx_memzero(&pr->pattern.complex,
                         sizeof(ngx_http_complex_value_t));
 
             ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t));
 
-            pr->handler = ngx_http_proxy_rewrite_redirect_complex;
+            pr->handler = ngx_http_proxy_rewrite_complex_handler;
 
             if (conf->vars.uri.len) {
-                pr->redirect.complex.value = conf->url;
+                pr->pattern.complex.value = conf->url;
                 pr->replacement.value = conf->location;
 
             } else {
-                pr->redirect.complex.value.len = conf->url.len
-                                                 + sizeof("/") - 1;
-
-                p = ngx_pnalloc(cf->pool, pr->redirect.complex.value.len);
+                pr->pattern.complex.value.len = conf->url.len
+                                                + sizeof("/") - 1;
+
+                p = ngx_pnalloc(cf->pool, pr->pattern.complex.value.len);
                 if (p == NULL) {
                     return NGX_CONF_ERROR;
                 }
 
-                pr->redirect.complex.value.data = p;
+                pr->pattern.complex.value.data = p;
 
                 p = ngx_cpymem(p, conf->url.data, conf->url.len);
                 *p = '/';
@@ -2800,6 +2958,10 @@
         }
     }
 
+    ngx_conf_merge_ptr_value(conf->cookie_domains, prev->cookie_domains, NULL);
+
+    ngx_conf_merge_ptr_value(conf->cookie_paths, prev->cookie_paths, NULL);
+
 #if (NGX_HTTP_SSL)
     if (conf->upstream.ssl == NULL) {
         conf->upstream.ssl = prev->upstream.ssl;
@@ -3285,7 +3447,7 @@
 
     u_char                            *p;
     ngx_str_t                         *value;
-    ngx_http_proxy_redirect_t         *pr;
+    ngx_http_proxy_rewrite_t          *pr;
     ngx_http_compile_complex_value_t   ccv;
 
     if (plcf->redirect == 0) {
@@ -3320,7 +3482,7 @@
 
     if (plcf->redirects == NULL) {
         plcf->redirects = ngx_array_create(cf->pool, 1,
-                                           sizeof(ngx_http_proxy_redirect_t));
+                                           sizeof(ngx_http_proxy_rewrite_t));
         if (plcf->redirects == NULL) {
             return NGX_CONF_ERROR;
         }
@@ -3346,25 +3508,25 @@
             return NGX_CONF_ERROR;
         }
 
-        pr->handler = ngx_http_proxy_rewrite_redirect_complex;
-
-        ngx_memzero(&pr->redirect.complex, sizeof(ngx_http_complex_value_t));
+        pr->handler = ngx_http_proxy_rewrite_complex_handler;
+
+        ngx_memzero(&pr->pattern.complex, sizeof(ngx_http_complex_value_t));
 
         ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t));
 
         if (plcf->vars.uri.len) {
-            pr->redirect.complex.value = plcf->url;
+            pr->pattern.complex.value = plcf->url;
             pr->replacement.value = plcf->location;
 
         } else {
-            pr->redirect.complex.value.len = plcf->url.len + sizeof("/") - 1;
-
-            p = ngx_pnalloc(cf->pool, pr->redirect.complex.value.len);
+            pr->pattern.complex.value.len = plcf->url.len + sizeof("/") - 1;
+
+            p = ngx_pnalloc(cf->pool, pr->pattern.complex.value.len);
             if (p == NULL) {
                 return NGX_CONF_ERROR;
             }
 
-            pr->redirect.complex.value.data = p;
+            pr->pattern.complex.value.data = p;
 
             p = ngx_cpymem(p, plcf->url.data, plcf->url.len);
             *p = '/';
@@ -3377,52 +3539,36 @@
 
 
     if (value[1].data[0] == '~') {
-#if (NGX_PCRE)
-        u_char               errstr[NGX_MAX_CONF_ERRSTR];
-        ngx_regex_compile_t  rc;
-
         value[1].len--;
         value[1].data++;
 
-        ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
-
         if (value[1].data[0] == '*') {
             value[1].len--;
             value[1].data++;
-            rc.options = NGX_REGEX_CASELESS;
-        }
-
-        rc.pattern = value[1];
-        rc.err.len = NGX_MAX_CONF_ERRSTR;
-        rc.err.data = errstr;
-
-        pr->redirect.regex = ngx_http_regex_compile(cf, &rc);
-        if (pr->redirect.regex == NULL) {
-            return NGX_CONF_ERROR;
+
+            if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {
+                return NGX_CONF_ERROR;
+            }
+
+        } else {
+            if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 0) != NGX_OK) {
+                return NGX_CONF_ERROR;
+            }
         }
 
-        pr->handler = ngx_http_proxy_rewrite_redirect_regex;
-
-#else
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           "using regex \"%V\" requires PCRE library",
-                           &value[1]);
-
-        return NGX_CONF_ERROR;
-#endif
     } else {
 
         ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
 
         ccv.cf = cf;
         ccv.value = &value[1];
-        ccv.complex_value = &pr->redirect.complex;
+        ccv.complex_value = &pr->pattern.complex;
 
         if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
             return NGX_CONF_ERROR;
         }
 
-        pr->handler = ngx_http_proxy_rewrite_redirect_complex;
+        pr->handler = ngx_http_proxy_rewrite_complex_handler;
     }
 
 
@@ -3441,6 +3587,217 @@
 
 
 static char *
+ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_proxy_loc_conf_t *plcf = conf;
+
+    ngx_str_t                         *value;
+    ngx_http_proxy_rewrite_t          *pr;
+    ngx_http_compile_complex_value_t   ccv;
+
+    if (plcf->cookie_domains == NULL) {
+        return NGX_CONF_OK;
+    }
+
+    value = cf->args->elts;
+
+    if (cf->args->nelts == 2) {
+
+        if (ngx_strcmp(value[1].data, "off") == 0) {
+            plcf->cookie_domains = NULL;
+            return NGX_CONF_OK;
+        }
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid parameter \"%V\"", &value[1]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (plcf->cookie_domains == NGX_CONF_UNSET_PTR) {
+        plcf->cookie_domains = ngx_array_create(cf->pool, 1,
+                                     sizeof(ngx_http_proxy_rewrite_t));
+        if (plcf->cookie_domains == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    pr = ngx_array_push(plcf->cookie_domains);
+    if (pr == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (value[1].data[0] == '~') {
+        value[1].len--;
+        value[1].data++;
+
+        if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+
+    } else {
+
+        if (value[1].data[0] == '.') {
+            value[1].len--;
+            value[1].data++;
+        }
+
+        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+        ccv.cf = cf;
+        ccv.value = &value[1];
+        ccv.complex_value = &pr->pattern.complex;
+
+        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+
+        pr->handler = ngx_http_proxy_rewrite_domain_handler;
+
+        if (value[2].data[0] == '.') {
+            value[2].len--;
+            value[2].data++;
+        }
+    }
+
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &value[2];
+    ccv.complex_value = &pr->replacement;
+
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_proxy_loc_conf_t *plcf = conf;
+
+    ngx_str_t                         *value;
+    ngx_http_proxy_rewrite_t          *pr;
+    ngx_http_compile_complex_value_t   ccv;
+
+    if (plcf->cookie_paths == NULL) {
+        return NGX_CONF_OK;
+    }
+
+    value = cf->args->elts;
+
+    if (cf->args->nelts == 2) {
+
+        if (ngx_strcmp(value[1].data, "off") == 0) {
+            plcf->cookie_paths = NULL;
+            return NGX_CONF_OK;
+        }
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid parameter \"%V\"", &value[1]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (plcf->cookie_paths == NGX_CONF_UNSET_PTR) {
+        plcf->cookie_paths = ngx_array_create(cf->pool, 1,
+                                     sizeof(ngx_http_proxy_rewrite_t));
+        if (plcf->cookie_paths == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    pr = ngx_array_push(plcf->cookie_paths);
+    if (pr == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (value[1].data[0] == '~') {
+        value[1].len--;
+        value[1].data++;
+
+        if (value[1].data[0] == '*') {
+            value[1].len--;
+            value[1].data++;
+
+            if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {
+                return NGX_CONF_ERROR;
+            }
+
+        } else {
+            if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 0) != NGX_OK) {
+                return NGX_CONF_ERROR;
+            }
+        }
+
+    } else {
+
+        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+        ccv.cf = cf;
+        ccv.value = &value[1];
+        ccv.complex_value = &pr->pattern.complex;
+
+        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+
+        pr->handler = ngx_http_proxy_rewrite_complex_handler;
+    }
+
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &value[2];
+    ccv.complex_value = &pr->replacement;
+
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_rewrite_regex(ngx_conf_t *cf, ngx_http_proxy_rewrite_t *pr,
+    ngx_str_t *regex, ngx_uint_t caseless)
+{
+#if (NGX_PCRE)
+    u_char               errstr[NGX_MAX_CONF_ERRSTR];
+    ngx_regex_compile_t  rc;
+
+    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+    rc.pattern = *regex;
+    rc.err.len = NGX_MAX_CONF_ERRSTR;
+    rc.err.data = errstr;
+
+    if (caseless) {
+        rc.options = NGX_REGEX_CASELESS;
+    }
+
+    pr->pattern.regex = ngx_http_regex_compile(cf, &rc);
+    if (pr->pattern.regex == NULL) {
+        return NGX_ERROR;
+    }
+
+    pr->handler = ngx_http_proxy_rewrite_regex_handler;
+
+    return NGX_OK;
+
+#else
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "using regex \"%V\" requires PCRE library", regex);
+    return NGX_ERROR;
+
+#endif
+}
+
+
+static char *
 ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_http_proxy_loc_conf_t *plcf = conf;
--- a/src/http/modules/ngx_http_range_filter_module.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/ngx_http_range_filter_module.c	Wed Feb 15 00:00:00 2012 +0400
@@ -595,15 +595,8 @@
     buf = in->buf;
 
     if (!buf->last_buf) {
-
-        if (buf->in_file) {
-            start = buf->file_pos + ctx->offset;
-            last = buf->file_last + ctx->offset;
-
-        } else {
-            start = buf->pos - buf->start + ctx->offset;
-            last = buf->last - buf->start + ctx->offset;
-        }
+        start = ctx->offset;
+        last = ctx->offset + ngx_buf_size(buf);
 
         range = ctx->ranges.elts;
         for (i = 0; i < ctx->ranges.nelts; i++) {
@@ -716,7 +709,6 @@
 ngx_http_range_multipart_body(ngx_http_request_t *r,
     ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
 {
-    off_t              body_start;
     ngx_buf_t         *b, *buf;
     ngx_uint_t         i;
     ngx_chain_t       *out, *hcl, *rcl, *dcl, **ll;
@@ -726,12 +718,6 @@
     buf = in->buf;
     range = ctx->ranges.elts;
 
-#if (NGX_HTTP_CACHE)
-    body_start = r->cached ? r->cache->body_start : 0;
-#else
-    body_start = 0;
-#endif
-
     for (i = 0; i < ctx->ranges.nelts; i++) {
 
         /*
@@ -792,13 +778,13 @@
         b->file = buf->file;
 
         if (buf->in_file) {
-            b->file_pos = body_start + range[i].start;
-            b->file_last = body_start + range[i].end;
+            b->file_pos = buf->file_pos + range[i].start;
+            b->file_last = buf->file_pos + range[i].end;
         }
 
         if (ngx_buf_in_memory(buf)) {
-            b->pos = buf->start + (size_t) range[i].start;
-            b->last = buf->start + (size_t) range[i].end;
+            b->pos = buf->pos + (size_t) range[i].start;
+            b->last = buf->pos + (size_t) range[i].end;
         }
 
         dcl = ngx_alloc_chain_link(r->pool);
--- a/src/http/modules/ngx_http_static_module.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/ngx_http_static_module.c	Wed Feb 15 00:00:00 2012 +0400
@@ -94,6 +94,9 @@
     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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
         != NGX_OK)
@@ -112,6 +115,10 @@
             break;
 
         case NGX_EACCES:
+#if (NGX_HAVE_OPENAT)
+        case NGX_EMLINK:
+        case NGX_ELOOP:
+#endif
 
             level = NGX_LOG_ERR;
             rc = NGX_HTTP_FORBIDDEN;
--- a/src/http/modules/ngx_http_userid_filter_module.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/ngx_http_userid_filter_module.c	Wed Feb 15 00:00:00 2012 +0400
@@ -774,14 +774,10 @@
     }
 
     ucf->expires = ngx_parse_time(&value[1], 1);
-    if (ucf->expires == NGX_ERROR) {
+    if (ucf->expires == (time_t) NGX_ERROR) {
         return "invalid value";
     }
 
-    if (ucf->expires == NGX_PARSE_LARGE_TIME) {
-        return "value must be less than 68 years";
-    }
-
     return NGX_CONF_OK;
 }
 
--- a/src/http/modules/perl/Makefile.PL	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/perl/Makefile.PL	Wed Feb 15 00:00:00 2012 +0400
@@ -25,7 +25,11 @@
                          "-I ../../../../../$ENV{NGX_OBJS} " .
                          ($ENV{NGX_PCRE} =~ /^(YES|NO)/ ? "" :
                              ($ENV{NGX_PCRE} =~ m#^/# ? "-I $ENV{NGX_PCRE} " :
-                                  "-I ../../../../../$ENV{NGX_PCRE} ")),
+                                  "-I ../../../../../$ENV{NGX_PCRE} ")) .
+                         ($ENV{NGX_OPENSSL} =~ /^(YES|NO)/ ? "" :
+                             ($ENV{NGX_OPENSSL} =~ m#^/# ?
+                                  "-I $ENV{NGX_OPENSSL}/.openssl/include " :
+                      "-I ../../../../../$ENV{NGX_OPENSSL}/.openssl/include ")),
 
     depend => {
         'nginx.c'     =>
--- a/src/http/modules/perl/nginx.pm	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/perl/nginx.pm	Wed Feb 15 00:00:00 2012 +0400
@@ -48,7 +48,7 @@
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '1.1.14';
+our $VERSION = '1.1.15';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/modules/perl/nginx.xs	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/modules/perl/nginx.xs	Wed Feb 15 00:00:00 2012 +0400
@@ -662,6 +662,9 @@
     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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
         != NGX_OK)
--- a/src/http/ngx_http_busy_lock.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/ngx_http_busy_lock.c	Wed Feb 15 00:00:00 2012 +0400
@@ -274,7 +274,7 @@
             line.data = value[i].data + 2;
 
             bl->timeout = ngx_parse_time(&line, 1);
-            if (bl->timeout == NGX_ERROR) {
+            if (bl->timeout == (time_t) NGX_ERROR) {
                 invalid = 1;
                 break;
             }
--- a/src/http/ngx_http_core_module.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/ngx_http_core_module.c	Wed Feb 15 00:00:00 2012 +0400
@@ -187,6 +187,18 @@
 #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"),
@@ -764,6 +776,17 @@
 
 #endif
 
+#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_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, disable_symlinks),
+      &ngx_http_core_disable_symlinks },
+
+#endif
+
       ngx_null_command
 };
 
@@ -1297,6 +1320,9 @@
         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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
             != NGX_OK)
@@ -2453,7 +2479,6 @@
     sr->start_sec = tp->sec;
     sr->start_msec = tp->msec;
 
-    r->main->subrequests++;
     r->main->count++;
 
     *psr = sr;
@@ -2525,6 +2550,16 @@
     ngx_http_core_main_conf_t   *cmcf;
 
     r->main->count++;
+    r->uri_changes--;
+
+    if (r->uri_changes == 0) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "rewrite or internal redirection cycle "
+                      "while redirect to named location \"%V\"", name);
+
+        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return NGX_DONE;
+    }
 
     cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
 
@@ -3335,6 +3370,10 @@
 #endif
 #endif
 
+#if (NGX_HAVE_OPENAT)
+    clcf->disable_symlinks = NGX_CONF_UNSET_UINT;
+#endif
+
     return clcf;
 }
 
@@ -3614,6 +3653,11 @@
 #endif
 #endif
 
+#if (NGX_HAVE_OPENAT)
+    ngx_conf_merge_uint_value(conf->disable_symlinks, prev->disable_symlinks,
+                              NGX_DISABLE_SYMLINKS_OFF);
+#endif
+
     return NGX_CONF_OK;
 }
 
@@ -3844,7 +3888,7 @@
                     s.len = p - s.data;
 
                     lsopt.tcp_keepidle = ngx_parse_time(&s, 1);
-                    if (lsopt.tcp_keepidle == NGX_ERROR) {
+                    if (lsopt.tcp_keepidle == (time_t) NGX_ERROR) {
                         goto invalid_so_keepalive;
                     }
                 }
@@ -3860,7 +3904,7 @@
                     s.len = p - s.data;
 
                     lsopt.tcp_keepintvl = ngx_parse_time(&s, 1);
-                    if (lsopt.tcp_keepintvl == NGX_ERROR) {
+                    if (lsopt.tcp_keepintvl == (time_t) NGX_ERROR) {
                         goto invalid_so_keepalive;
                     }
                 }
@@ -4507,7 +4551,7 @@
             s.data = value[i].data + 9;
 
             inactive = ngx_parse_time(&s, 1);
-            if (inactive < 0) {
+            if (inactive == (time_t) NGX_ERROR) {
                 goto failed;
             }
 
@@ -4594,24 +4638,16 @@
         return "invalid value";
     }
 
-    if (clcf->keepalive_timeout == (ngx_msec_t) NGX_PARSE_LARGE_TIME) {
-        return "value must be less than 597 hours";
-    }
-
     if (cf->args->nelts == 2) {
         return NGX_CONF_OK;
     }
 
     clcf->keepalive_header = ngx_parse_time(&value[2], 1);
 
-    if (clcf->keepalive_header == NGX_ERROR) {
+    if (clcf->keepalive_header == (time_t) NGX_ERROR) {
         return "invalid value";
     }
 
-    if (clcf->keepalive_header == NGX_PARSE_LARGE_TIME) {
-        return "value must be less than 68 years";
-    }
-
     return NGX_CONF_OK;
 }
 
--- a/src/http/ngx_http_core_module.h	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/ngx_http_core_module.h	Wed Feb 15 00:00:00 2012 +0400
@@ -404,6 +404,10 @@
 #endif
 #endif
 
+#if (NGX_HAVE_OPENAT)
+    ngx_uint_t    disable_symlinks;        /* disable_symlinks */
+#endif
+
     ngx_array_t  *error_pages;             /* error_page */
     ngx_http_try_file_t    *try_files;     /* try_files */
 
--- a/src/http/ngx_http_file_cache.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/ngx_http_file_cache.c	Wed Feb 15 00:00:00 2012 +0400
@@ -1597,7 +1597,8 @@
     time_t                  inactive;
     ssize_t                 size;
     ngx_str_t               s, name, *value;
-    ngx_int_t               loader_files, loader_sleep, loader_threshold;
+    ngx_int_t               loader_files;
+    ngx_msec_t              loader_sleep, loader_threshold;
     ngx_uint_t              i, n;
     ngx_http_file_cache_t  *cache;
 
@@ -1704,7 +1705,7 @@
             s.data = value[i].data + 9;
 
             inactive = ngx_parse_time(&s, 1);
-            if (inactive < 0) {
+            if (inactive == (time_t) NGX_ERROR) {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                    "invalid inactive value \"%V\"", &value[i]);
                 return NGX_CONF_ERROR;
@@ -1746,7 +1747,7 @@
             s.data = value[i].data + 13;
 
             loader_sleep = ngx_parse_time(&s, 0);
-            if (loader_sleep < 0) {
+            if (loader_sleep == (ngx_msec_t) NGX_ERROR) {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid loader_sleep value \"%V\"", &value[i]);
                 return NGX_CONF_ERROR;
@@ -1761,7 +1762,7 @@
             s.data = value[i].data + 17;
 
             loader_threshold = ngx_parse_time(&s, 0);
-            if (loader_threshold < 0) {
+            if (loader_threshold == (ngx_msec_t) NGX_ERROR) {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid loader_threshold value \"%V\"", &value[i]);
                 return NGX_CONF_ERROR;
@@ -1788,8 +1789,8 @@
     cache->path->conf_file = cf->conf_file->file.name.data;
     cache->path->line = cf->conf_file->line;
     cache->loader_files = loader_files;
-    cache->loader_sleep = (ngx_msec_t) loader_sleep;
-    cache->loader_threshold = (ngx_msec_t) loader_threshold;
+    cache->loader_sleep = loader_sleep;
+    cache->loader_threshold = loader_threshold;
 
     if (ngx_add_path(cf, &cache->path) != NGX_OK) {
         return NGX_CONF_ERROR;
@@ -1843,7 +1844,7 @@
     n = cf->args->nelts - 1;
 
     valid = ngx_parse_time(&value[n], 1);
-    if (valid < 0) {
+    if (valid == (time_t) NGX_ERROR) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid time value \"%V\"", &value[n]);
         return NGX_CONF_ERROR;
--- a/src/http/ngx_http_request.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/ngx_http_request.c	Wed Feb 15 00:00:00 2012 +0400
@@ -2010,6 +2010,7 @@
         if (r == c->data) {
 
             r->main->count--;
+            r->main->subrequests++;
 
             if (!r->logged) {
 
@@ -2927,6 +2928,10 @@
         return NGX_DECLINED;
     }
 
+    if (r->post_action && r->uri_changes == 0) {
+        return NGX_DECLINED;
+    }
+
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "post action: \"%V\"", &clcf->post_action);
 
--- a/src/http/ngx_http_request.h	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/ngx_http_request.h	Wed Feb 15 00:00:00 2012 +0400
@@ -478,7 +478,7 @@
 
     /*
      * instead of using the request context data in
-     * ngx_http_limit_zone_module and ngx_http_limit_req_module
+     * ngx_http_limit_conn_module and ngx_http_limit_req_module
      * we use the single bits in the request structure
      */
     unsigned                          limit_conn_set:1;
--- a/src/http/ngx_http_script.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/ngx_http_script.c	Wed Feb 15 00:00:00 2012 +0400
@@ -1505,6 +1505,9 @@
     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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
         != NGX_OK)
--- a/src/http/ngx_http_upstream.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/ngx_http_upstream.c	Wed Feb 15 00:00:00 2012 +0400
@@ -110,6 +110,8 @@
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
 
@@ -198,7 +200,7 @@
 
     { ngx_string("Set-Cookie"),
                  ngx_http_upstream_process_set_cookie, 0,
-                 ngx_http_upstream_copy_header_line, 0, 1 },
+                 ngx_http_upstream_rewrite_set_cookie, 0, 1 },
 
     { ngx_string("Content-Disposition"),
                  ngx_http_upstream_ignore_header_line, 0,
@@ -1591,7 +1593,7 @@
 
         if (rc == NGX_AGAIN) {
 
-            if (u->buffer.pos == u->buffer.end) {
+            if (u->buffer.last == u->buffer.end) {
                 ngx_log_error(NGX_LOG_ERR, c->log, 0,
                               "upstream sent too big header");
 
@@ -2649,7 +2651,6 @@
 static void
 ngx_http_upstream_process_request(ngx_http_request_t *r)
 {
-    ngx_uint_t            del;
     ngx_temp_file_t      *tf;
     ngx_event_pipe_t     *p;
     ngx_http_upstream_t  *u;
@@ -2661,30 +2662,16 @@
 
         if (u->store) {
 
-            del = p->upstream_error;
-
-            tf = u->pipe->temp_file;
-
             if (p->upstream_eof || p->upstream_done) {
 
+                tf = u->pipe->temp_file;
+
                 if (u->headers_in.status_n == NGX_HTTP_OK
                     && (u->headers_in.content_length_n == -1
                         || (u->headers_in.content_length_n == tf->offset)))
                 {
                     ngx_http_upstream_store(r, u);
-
-                } else {
-                    del = 1;
-                }
-            }
-
-            if (del && tf->file.fd != NGX_INVALID_FILE) {
-
-                if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) {
-
-                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
-                                  ngx_delete_file_n " \"%s\" failed",
-                                  u->pipe->temp_file->file.name.data);
+                    u->store = 0;
                 }
             }
         }
@@ -3047,6 +3034,18 @@
                        u->pipe->temp_file->file.fd);
     }
 
+    if (u->store && u->pipe && u->pipe->temp_file
+        && u->pipe->temp_file->file.fd != NGX_INVALID_FILE)
+    {
+        if (ngx_delete_file(u->pipe->temp_file->file.name.data)
+            == NGX_FILE_ERROR)
+        {
+            ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+                          ngx_delete_file_n " \"%s\" failed",
+                          u->pipe->temp_file->file.name.data);
+        }
+    }
+
 #if (NGX_HTTP_CACHE)
 
     if (r->cache) {
@@ -3673,6 +3672,27 @@
 
 
 static ngx_int_t
+ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
+    ngx_uint_t offset)
+{
+    ngx_table_elt_t  *ho;
+
+    ho = ngx_list_push(&r->headers_out.headers);
+    if (ho == NULL) {
+        return NGX_ERROR;
+    }
+
+    *ho = *h;
+
+    if (r->upstream->rewrite_cookie) {
+        return r->upstream->rewrite_cookie(r, ho);
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset)
 {
@@ -4248,7 +4268,7 @@
 
             fail_timeout = ngx_parse_time(&s, 1);
 
-            if (fail_timeout == NGX_ERROR) {
+            if (fail_timeout == (time_t) NGX_ERROR) {
                 goto invalid;
             }
 
--- a/src/http/ngx_http_upstream.h	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/ngx_http_upstream.h	Wed Feb 15 00:00:00 2012 +0400
@@ -299,6 +299,8 @@
                                          ngx_int_t rc);
     ngx_int_t                      (*rewrite_redirect)(ngx_http_request_t *r,
                                          ngx_table_elt_t *h, size_t prefix);
+    ngx_int_t                      (*rewrite_cookie)(ngx_http_request_t *r,
+                                         ngx_table_elt_t *h);
 
     ngx_msec_t                       timeout;
 
--- a/src/http/ngx_http_variables.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/http/ngx_http_variables.c	Wed Feb 15 00:00:00 2012 +0400
@@ -432,7 +432,7 @@
 
     v = &r->variables[index];
 
-    if (v->valid) {
+    if (v->valid || v->not_found) {
         if (!v->no_cacheable) {
             return v;
         }
--- a/src/mail/ngx_mail_core_module.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/mail/ngx_mail_core_module.c	Wed Feb 15 00:00:00 2012 +0400
@@ -479,7 +479,7 @@
                     s.len = p - s.data;
 
                     ls->tcp_keepidle = ngx_parse_time(&s, 1);
-                    if (ls->tcp_keepidle == NGX_ERROR) {
+                    if (ls->tcp_keepidle == (time_t) NGX_ERROR) {
                         goto invalid_so_keepalive;
                     }
                 }
@@ -495,7 +495,7 @@
                     s.len = p - s.data;
 
                     ls->tcp_keepintvl = ngx_parse_time(&s, 1);
-                    if (ls->tcp_keepintvl == NGX_ERROR) {
+                    if (ls->tcp_keepintvl == (time_t) NGX_ERROR) {
                         goto invalid_so_keepalive;
                     }
                 }
--- a/src/os/unix/ngx_errno.h	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/os/unix/ngx_errno.h	Wed Feb 15 00:00:00 2012 +0400
@@ -48,6 +48,11 @@
 #define NGX_EILSEQ        EILSEQ
 #define NGX_ENOMOREFILES  0
 
+#if (NGX_HAVE_OPENAT)
+#define NGX_EMLINK        EMLINK
+#define NGX_ELOOP         ELOOP
+#endif
+
 #if (__hpux__)
 #define NGX_EAGAIN        EWOULDBLOCK
 #else
--- a/src/os/unix/ngx_files.h	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/os/unix/ngx_files.h	Wed Feb 15 00:00:00 2012 +0400
@@ -76,6 +76,10 @@
 #define NGX_FILE_APPEND          O_WRONLY|O_APPEND
 #define NGX_FILE_NONBLOCK        O_NONBLOCK
 
+#if (NGX_HAVE_OPENAT)
+#define NGX_FILE_NOFOLLOW        O_NOFOLLOW
+#endif
+
 #define NGX_FILE_DEFAULT_ACCESS  0644
 #define NGX_FILE_OWNER_ACCESS    0600
 
@@ -324,6 +328,21 @@
 size_t ngx_fs_bsize(u_char *name);
 
 
+#if (NGX_HAVE_OPENAT)
+
+#define ngx_openat_file(fd, name, mode, create, access)                      \
+    openat(fd, (const char *) name, mode|create, access)
+
+#define ngx_openat_file_n        "openat()"
+
+#define ngx_file_at_info(fd, name, sb, flag)                                 \
+    fstatat(fd, (const char *) name, sb, flag)
+
+#define ngx_file_at_info_n       "fstatat()"
+
+#endif
+
+
 #define ngx_stderr               STDERR_FILENO
 #define ngx_set_stderr(fd)       dup2(fd, STDERR_FILENO)
 #define ngx_set_stderr_n         "dup2(STDERR_FILENO)"
--- a/src/os/unix/ngx_posix_init.c	Mon Jan 30 00:00:00 2012 +0400
+++ b/src/os/unix/ngx_posix_init.c	Wed Feb 15 00:00:00 2012 +0400
@@ -47,7 +47,13 @@
 
     for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ }
 
+#if (NGX_HAVE_SC_NPROCESSORS_ONLN)
     if (ngx_ncpu == 0) {
+        ngx_ncpu = sysconf(_SC_NPROCESSORS_ONLN);
+    }
+#endif
+
+    if (ngx_ncpu < 1) {
         ngx_ncpu = 1;
     }