diff src/core/ngx_open_file_cache.c @ 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 d0f7a625f27c
children f5b859b2f097
line wrap: on
line diff
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -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 @@ ngx_open_cached_file(ngx_open_file_cache
 
         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 @@ ngx_open_cached_file(ngx_open_file_cache
             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 @@ ngx_open_cached_file(ngx_open_file_cache
 
             /* 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 @@ ngx_open_cached_file(ngx_open_file_cache
         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 @@ ngx_open_cached_file(ngx_open_file_cache
 
             } 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 @@ ngx_open_cached_file(ngx_open_file_cache
         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 @@ ngx_open_cached_file(ngx_open_file_cache
 
             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 @@ ngx_open_cached_file(ngx_open_file_cache
 
     /* 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 @@ update:
 
     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 @@ failed:
     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 @@ failed:
 }
 
 
+#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 @@ ngx_open_and_stat_file(u_char *name, ngx
 
     } 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 @@ ngx_open_and_stat_file(u_char *name, ngx
          * 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 @@ ngx_open_and_stat_file(u_char *name, ngx
     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 @@ ngx_open_and_stat_file(u_char *name, ngx
         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 @@ done:
     of->is_exec = ngx_is_exec(&fi);
 
     return NGX_OK;
-
-failed:
-
-    of->fd = NGX_INVALID_FILE;
-    of->err = ngx_errno;
-
-    return NGX_ERROR;
 }