Mercurial > hg > nginx-vendor-current
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; }