changeset 456:ca8f7f6cab16 NGINX_0_7_40

nginx 0.7.40 *) Feature: the "location" directive supports captures in regular expressions. *) Feature: an "alias" directive with capture references may be used inside a location given by a regular expression with captures. *) Feature: the "server_name" directive supports captures in regular expressions. *) Workaround: the ngx_http_autoindex_module did not show the trailing slash in directories on XFS filesystem; the issue had appeared in 0.7.15. Thanks to Dmitry Kuzmenko.
author Igor Sysoev <http://sysoev.ru>
date Mon, 09 Mar 2009 00:00:00 +0300
parents ead634c4b006
children cc8fb8cd98bb
files CHANGES CHANGES.ru src/core/nginx.h src/http/modules/ngx_http_rewrite_module.c src/http/modules/perl/nginx.pm src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_header_filter_module.c src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_script.c src/http/ngx_http_script.h src/os/unix/ngx_files.c src/os/unix/ngx_files.h
diffstat 14 files changed, 252 insertions(+), 135 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,21 @@
 
+Changes with nginx 0.7.40                                        09 Mar 2009
+
+    *) Feature: the "location" directive supports captures in regular 
+       expressions.
+
+    *) Feature: an "alias" directive with capture references may be used 
+       inside a location given by a regular expression with captures.
+
+    *) Feature: the "server_name" directive supports captures in regular 
+       expressions.
+
+    *) Workaround: the ngx_http_autoindex_module did not show the trailing 
+       slash in directories on XFS filesystem; the issue had appeared in 
+       0.7.15.
+       Thanks to Dmitry Kuzmenko.
+
+
 Changes with nginx 0.7.39                                        02 Mar 2009
 
     *) Bugfix: large response with SSI might hang, if gzipping was enabled; 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,22 @@
 
+Изменения в nginx 0.7.40                                          09.03.2009
+
+    *) Добавление: директива location поддерживает выделения в регулярных 
+       выражениях.
+
+    *) Добавление: директиву alias с ссылками на выделения в регулярных 
+       выражениях можно использовать внутри location'а, заданного 
+       регулярным выражением с выделениями.
+
+    *) Добавление: директива server_name поддерживает выделения в 
+       регулярных выражениях.
+
+    *) Изменение: модуль ngx_http_autoindex_module не показывал последний 
+       слэш для каталогов на файловой системе XFS; ошибка появилась в 
+       0.7.15.
+       Спасибо Дмитрию Кузьменко.
+
+
 Изменения в nginx 0.7.39                                          02.03.2009
 
     *) Исправление: при включённом сжатии большие ответы с использованием 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.7.39"
+#define NGINX_VERSION      "0.7.40"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/http/modules/ngx_http_rewrite_module.c
+++ b/src/http/modules/ngx_http_rewrite_module.c
@@ -12,7 +12,6 @@
 typedef struct {
     ngx_array_t  *codes;        /* uintptr_t */
 
-    ngx_uint_t    captures;
     ngx_uint_t    stack_size;
 
     ngx_flag_t    log;
@@ -157,16 +156,6 @@ ngx_http_rewrite_handler(ngx_http_reques
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    if (rlcf->captures) {
-        e->captures = ngx_palloc(r->pool, rlcf->captures * sizeof(int));
-        if (e->captures == NULL) {
-            return NGX_HTTP_INTERNAL_SERVER_ERROR;
-        }
-
-    } else {
-        e->captures = NULL;
-    }
-
     e->ip = rlcf->codes->elts;
     e->request = r;
     e->quote = 1;
@@ -436,10 +425,6 @@ ngx_http_rewrite(ngx_conf_t *cf, ngx_com
 
     if (regex->ncaptures) {
         regex->ncaptures = (regex->ncaptures + 1) * 3;
-
-        if (lcf->captures < regex->ncaptures) {
-            lcf->captures = regex->ncaptures;
-        }
     }
 
     regex_end = ngx_http_script_add_code(lcf->codes,
@@ -618,11 +603,6 @@ ngx_http_rewrite_if(ngx_conf_t *cf, ngx_
     }
 
 
-    if (lcf->captures < nlcf->captures) {
-        lcf->captures = nlcf->captures;
-    }
-
-
     if (elts != lcf->codes->elts) {
         if_code = (ngx_http_script_if_code_t *)
                    ((u_char *) if_code + ((u_char *) lcf->codes->elts - elts));
@@ -777,10 +757,6 @@ ngx_http_rewrite_if_condition(ngx_conf_t
 
             if (n) {
                 regex->ncaptures = (n + 1) * 3;
-
-                if (lcf->captures < regex->ncaptures) {
-                    lcf->captures = regex->ncaptures;
-                }
             }
 
             return NGX_CONF_OK;
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.7.39';
+our $VERSION = '0.7.40';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -1337,7 +1337,7 @@ ngx_http_core_find_location(ngx_http_req
     ngx_int_t                  rc;
     ngx_http_core_loc_conf_t  *pclcf;
 #if (NGX_PCRE)
-    ngx_int_t                  n;
+    ngx_int_t                  n, len;
     ngx_uint_t                 noregex;
     ngx_http_core_loc_conf_t  *clcf, **clcfp;
 
@@ -1371,12 +1371,24 @@ ngx_http_core_find_location(ngx_http_req
 
     if (noregex == 0 && pclcf->regex_locations) {
 
+        len = 0;
+
         for (clcfp = pclcf->regex_locations; *clcfp; clcfp++) {
 
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "test location: ~ \"%V\"", &(*clcfp)->name);
 
-            n = ngx_regex_exec((*clcfp)->regex, &r->uri, NULL, 0);
+            if ((*clcfp)->captures && r->captures == NULL) {
+
+                len = (NGX_HTTP_MAX_CAPTURES + 1) * 3 * sizeof(int);
+
+                r->captures = ngx_palloc(r->pool, len);
+                if (r->captures == NULL) {
+                    return NGX_ERROR;
+                }
+            }
+
+            n = ngx_regex_exec((*clcfp)->regex, &r->uri, r->captures, len);
 
             if (n == NGX_REGEX_NO_MATCHED) {
                 continue;
@@ -1394,6 +1406,9 @@ ngx_http_core_find_location(ngx_http_req
 
             r->loc_conf = (*clcfp)->loc_conf;
 
+            r->ncaptures = len;
+            r->captures_data = r->uri.data;
+
             /* look up nested locations */
 
             rc = ngx_http_core_find_location(r);
@@ -1686,6 +1701,11 @@ ngx_http_map_uri_to_path(ngx_http_reques
 
         *root_length = path->len - reserved;
         last = path->data + *root_length;
+
+        if (alias) {
+            *last = '\0';
+            return last;
+        }
     }
 
     last = ngx_cpystrn(last, r->uri.data + alias, r->uri.len - alias + 1);
@@ -2534,6 +2554,7 @@ ngx_http_core_regex_location(ngx_conf_t 
     }
 
     clcf->name = *regex;
+    clcf->captures = (ngx_regex_capture_count(clcf->regex) > 0);
 
     return NGX_OK;
 
@@ -2798,6 +2819,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t 
 
 #if (NGX_PCRE)
         sn->regex = NULL;
+        sn->captures = 0;
 #endif
         sn->core_srv_conf = conf;
         sn->name.len = conf->server_name.len;
@@ -3399,6 +3421,7 @@ ngx_http_core_server_name(ngx_conf_t *cf
 
 #if (NGX_PCRE)
         sn->regex = NULL;
+        sn->captures = 0;
 #endif
         sn->core_srv_conf = cscf;
         sn->name = value[i];
@@ -3425,6 +3448,7 @@ ngx_http_core_server_name(ngx_conf_t *cf
             return NGX_CONF_ERROR;
         }
 
+        sn->captures = (ngx_regex_capture_count(sn->regex) > 0);
         sn->name = value[i];
         }
 #else
@@ -3477,18 +3501,6 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c
         return NGX_CONF_ERROR;
     }
 
-#if (NGX_PCRE)
-
-    if (lcf->regex && alias) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           "the \"alias\" directive may not be used "
-                           "inside location given by regular expression");
-
-        return NGX_CONF_ERROR;
-    }
-
-#endif
-
     value = cf->args->elts;
 
     if (ngx_strstr(value[1].data, "$document_root")
@@ -3528,24 +3540,36 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c
 
     n = ngx_http_script_variables_count(&lcf->root);
 
-    if (n == 0) {
-        return NGX_CONF_OK;
+    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+    if (n) {
+        sc.cf = cf;
+        sc.source = &lcf->root;
+        sc.lengths = &lcf->root_lengths;
+        sc.values = &lcf->root_values;
+        sc.variables = n;
+        sc.complete_lengths = 1;
+        sc.complete_values = 1;
+
+        if (ngx_http_script_compile(&sc) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
     }
 
-    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
-
-    sc.cf = cf;
-    sc.source = &lcf->root;
-    sc.lengths = &lcf->root_lengths;
-    sc.values = &lcf->root_values;
-    sc.variables = n;
-    sc.complete_lengths = 1;
-    sc.complete_values = 1;
-
-    if (ngx_http_script_compile(&sc) != NGX_OK) {
+#if (NGX_PCRE)
+
+    if (alias && lcf->regex
+        && (ngx_regex_capture_count(lcf->regex) <= 0 || sc.ncaptures == 0))
+    {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "the \"alias\" directive must use captures "
+                           "inside location given by regular expression");
+
         return NGX_CONF_ERROR;
     }
 
+#endif
+
     return NGX_CONF_OK;
 }
 
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -245,6 +245,7 @@ typedef struct {
 struct ngx_http_server_name_s {
 #if (NGX_PCRE)
     ngx_regex_t               *regex;
+    ngx_uint_t                 captures;      /* unsigned  captures:1; */
 #endif
     ngx_http_core_srv_conf_t  *core_srv_conf; /* virtual name server conf */
     ngx_str_t                  name;
@@ -274,6 +275,8 @@ struct ngx_http_core_loc_conf_s {
 
 #if (NGX_PCRE)
     ngx_regex_t  *regex;
+
+    unsigned      captures:1;
 #endif
 
     unsigned      noname:1;   /* "if () {}" block or limit_except */
@@ -289,7 +292,9 @@ struct ngx_http_core_loc_conf_s {
 #endif
 
     ngx_http_location_tree_node_t   *static_locations;
+#if (NGX_PCRE)
     ngx_http_core_loc_conf_t       **regex_locations;
+#endif
 
     /* pointer to the modules' loc_conf */
     void        **loc_conf;
--- a/src/http/ngx_http_header_filter_module.c
+++ b/src/http/ngx_http_header_filter_module.c
@@ -322,12 +322,12 @@ ngx_http_header_filter(ngx_http_request_
         if (clcf->port_in_redirect) {
 
 #if (NGX_HTTP_SSL)
-	    if (r->connection->ssl)
-		port = (port == 443) ? 0 : port;
-	    else
+            if (r->connection->ssl)
+                port = (port == 443) ? 0 : port;
+            else
 #endif
-		port = (port == 80) ? 0 : port;
-	}
+                port = (port == 80) ? 0 : port;
+        }
 
         if (port) {
             len += sizeof(":65535") - 1;
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1611,6 +1611,7 @@ static ngx_int_t
 ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len)
 {
     u_char                    *server;
+    size_t                     ncaptures;
     ngx_uint_t                 hash;
     ngx_http_virtual_names_t  *vn;
     ngx_http_core_loc_conf_t  *clcf;
@@ -1653,11 +1654,33 @@ ngx_http_find_virtual_server(ngx_http_re
         name.len = len;
         name.data = server;
 
+        ncaptures = 0;
+
         sn = vn->regex;
 
         for (i = 0; i < vn->nregex; i++) {
 
-            n = ngx_regex_exec(sn[i].regex, &name, NULL, 0);
+            if (sn[i].captures && r->captures == NULL) {
+
+                ncaptures = (NGX_HTTP_MAX_CAPTURES + 1) * 3 * sizeof(int);
+
+                r->captures = ngx_palloc(r->pool, ncaptures);
+                if (r->captures == NULL) {
+                    return NGX_ERROR;
+                }
+
+                if (server == buf) {
+                    server = ngx_pnalloc(r->pool, len);
+                    if (server == NULL) {
+                        return NGX_ERROR;
+                    }
+
+                    ngx_memcpy(server, buf, len);
+                    name.data = server;
+                }
+            }
+
+            n = ngx_regex_exec(sn[i].regex, &name, r->captures, ncaptures);
 
             if (n == NGX_REGEX_NO_MATCHED) {
                 continue;
@@ -1675,6 +1698,9 @@ ngx_http_find_virtual_server(ngx_http_re
 
             cscf = sn[i].core_srv_conf;
 
+            r->ncaptures = ncaptures;
+            r->captures_data = server;
+
             goto found;
         }
     }
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -10,6 +10,7 @@
 
 #define NGX_HTTP_MAX_URI_CHANGES           10
 #define NGX_HTTP_MAX_SUBREQUESTS           50
+#define NGX_HTTP_MAX_CAPTURES              9
 
 /* must be 2^n */
 #define NGX_HTTP_LC_HEADER_LEN             32
@@ -390,6 +391,12 @@ struct ngx_http_request_s {
 
     ngx_http_variable_value_t        *variables;
 
+#if (NGX_PCRE)
+    ngx_uint_t                        ncaptures;
+    int                              *captures;
+    u_char                           *captures_data;
+#endif
+
     size_t                            limit_rate;
 
     /* used to learn the Apache compatible response length without a header */
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -40,7 +40,9 @@ ngx_http_script_compile(ngx_http_script_
     ngx_uint_t                            i, n, bracket;
     ngx_http_script_var_code_t           *var_code;
     ngx_http_script_copy_code_t          *copy;
+#if (NGX_PCRE)
     ngx_http_script_copy_capture_code_t  *copy_capture;
+#endif
 
     if (sc->flushes && *sc->flushes == NULL) {
         n = sc->variables ? sc->variables : 1;
@@ -89,6 +91,10 @@ ngx_http_script_compile(ngx_http_script_
                 goto invalid_variable;
             }
 
+#if (NGX_PCRE)
+
+            /* NGX_HTTP_MAX_CAPTURES is 9 */
+
             if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') {
 
                 n = sc->source->data[i] - '0';
@@ -130,6 +136,8 @@ ngx_http_script_compile(ngx_http_script_
                 continue;
             }
 
+#endif
+
             if (sc->source->data[i] == '{') {
                 bracket = 1;
 
@@ -519,65 +527,6 @@ ngx_http_script_copy_var_code(ngx_http_s
 
 
 size_t
-ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e)
-{
-    ngx_http_script_copy_capture_code_t  *code;
-
-    code = (ngx_http_script_copy_capture_code_t *) e->ip;
-
-    e->ip += sizeof(ngx_http_script_copy_capture_code_t);
-
-    if (code->n < e->ncaptures) {
-        if ((e->is_args || e->quote)
-            && (e->request->quoted_uri || e->request->plus_in_uri))
-        {
-            return e->captures[code->n + 1] - e->captures[code->n]
-                   + 2 * ngx_escape_uri(NULL,
-                                &e->line.data[e->captures[code->n]],
-                                e->captures[code->n + 1] - e->captures[code->n],
-                                NGX_ESCAPE_ARGS);
-        } else {
-            return e->captures[code->n + 1] - e->captures[code->n];
-        }
-    }
-
-    return 0;
-}
-
-
-void
-ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e)
-{
-    u_char                               *p;
-    ngx_http_script_copy_capture_code_t  *code;
-
-    code = (ngx_http_script_copy_capture_code_t *) e->ip;
-
-    e->ip += sizeof(ngx_http_script_copy_capture_code_t);
-
-    p = e->pos;
-
-    if (code->n < e->ncaptures) {
-        if ((e->is_args || e->quote)
-            && (e->request->quoted_uri || e->request->plus_in_uri))
-        {
-            e->pos = (u_char *) ngx_escape_uri(p,
-                                &e->line.data[e->captures[code->n]],
-                                e->captures[code->n + 1] - e->captures[code->n],
-                                NGX_ESCAPE_ARGS);
-        } else {
-            e->pos = ngx_copy(p,
-                              &e->line.data[e->captures[code->n]],
-                              e->captures[code->n + 1] - e->captures[code->n]);
-        }
-    }
-
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
-                   "http script capture: \"%*s\"", e->pos - p, p);
-}
-
-
-size_t
 ngx_http_script_mark_args_code(ngx_http_script_engine_t *e)
 {
     e->is_args = 1;
@@ -599,7 +548,6 @@ ngx_http_script_start_args_code(ngx_http
 }
 
 
-
 #if (NGX_PCRE)
 
 void
@@ -628,7 +576,18 @@ ngx_http_script_regex_start_code(ngx_htt
         e->line.data = e->sp->data;
     }
 
-    rc = ngx_regex_exec(code->regex, &e->line, e->captures, code->ncaptures);
+    if (code->ncaptures && r->captures == NULL) {
+
+        r->captures = ngx_palloc(r->pool,
+                                 (NGX_HTTP_MAX_CAPTURES + 1) * 3 * sizeof(int));
+        if (r->captures == NULL) {
+            e->ip = ngx_http_script_exit;
+            e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+            return;
+        }
+    }
+
+    rc = ngx_regex_exec(code->regex, &e->line, r->captures, code->ncaptures);
 
     if (rc == NGX_REGEX_NO_MATCHED) {
         if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
@@ -637,7 +596,7 @@ ngx_http_script_regex_start_code(ngx_htt
                           &code->name, &e->line);
         }
 
-        e->ncaptures = 0;
+        r->ncaptures = 0;
 
         if (code->test) {
             if (code->negative_test) {
@@ -674,7 +633,8 @@ ngx_http_script_regex_start_code(ngx_htt
                       "\"%V\" matches \"%V\"", &code->name, &e->line);
     }
 
-    e->ncaptures = code->ncaptures;
+    r->ncaptures = code->ncaptures;
+    r->captures_data = e->line.data;
 
     if (code->test) {
         if (code->negative_test) {
@@ -725,7 +685,7 @@ ngx_http_script_regex_start_code(ngx_htt
         }
 
         for (n = 1; n < (ngx_uint_t) rc; n++) {
-            e->buf.len += e->captures[2 * n + 1] - e->captures[2 * n];
+            e->buf.len += r->captures[2 * n + 1] - r->captures[2 * n];
         }
 
     } else {
@@ -734,8 +694,6 @@ ngx_http_script_regex_start_code(ngx_htt
         le.ip = code->lengths->elts;
         le.line = e->line;
         le.request = r;
-        le.captures = e->captures;
-        le.ncaptures = e->ncaptures;
         le.quote = code->redirect;
 
         len = 0;
@@ -874,6 +832,84 @@ ngx_http_script_regex_end_code(ngx_http_
     e->ip += sizeof(ngx_http_script_regex_end_code_t);
 }
 
+
+size_t
+ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e)
+{
+    int                                  *cap;
+    u_char                               *p;
+    ngx_uint_t                            n;
+    ngx_http_request_t                   *r;
+    ngx_http_script_copy_capture_code_t  *code;
+
+    r = e->request;
+
+    code = (ngx_http_script_copy_capture_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_script_copy_capture_code_t);
+
+    n = code->n;
+
+    if (n < r->ncaptures) {
+
+        cap = r->captures;
+
+        if ((e->is_args || e->quote)
+            && (e->request->quoted_uri || e->request->plus_in_uri))
+        {
+            p = r->captures_data;
+
+            return cap[n + 1] - cap[n]
+                   + 2 * ngx_escape_uri(NULL, &p[cap[n]], cap[n + 1] - cap[n],
+                                        NGX_ESCAPE_ARGS);
+        } else {
+            return cap[n + 1] - cap[n];
+        }
+    }
+
+    return 0;
+}
+
+
+void
+ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e)
+{
+    int                                  *cap;
+    u_char                               *p, *pos;
+    ngx_uint_t                            n;
+    ngx_http_request_t                   *r;
+    ngx_http_script_copy_capture_code_t  *code;
+
+    r = e->request;
+
+    code = (ngx_http_script_copy_capture_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_script_copy_capture_code_t);
+
+    n = code->n;
+
+    pos = e->pos;
+
+    if (n < r->ncaptures) {
+
+        cap = r->captures;
+        p = r->captures_data;
+
+        if ((e->is_args || e->quote)
+            && (e->request->quoted_uri || e->request->plus_in_uri))
+        {
+            e->pos = (u_char *) ngx_escape_uri(pos, &p[cap[n]],
+                                               cap[n + 1] - cap[n],
+                                               NGX_ESCAPE_ARGS);
+        } else {
+            e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]);
+        }
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script capture: \"%*s\"", e->pos - pos, pos);
+}
+
 #endif
 
 
@@ -1133,8 +1169,6 @@ ngx_http_script_complex_value_code(ngx_h
     le.ip = code->lengths->elts;
     le.line = e->line;
     le.request = e->request;
-    le.captures = e->captures;
-    le.ncaptures = e->ncaptures;
     le.quote = e->quote;
 
     for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) {
--- a/src/http/ngx_http_script.h
+++ b/src/http/ngx_http_script.h
@@ -30,9 +30,6 @@ typedef struct {
     unsigned                    is_args:1;
     unsigned                    log:1;
 
-    int                        *captures;
-    ngx_uint_t                  ncaptures;
-
     ngx_int_t                   status;
     ngx_http_request_t         *request;
 } ngx_http_script_engine_t;
--- a/src/os/unix/ngx_files.c
+++ b/src/os/unix/ngx_files.c
@@ -249,11 +249,6 @@ ngx_open_dir(ngx_str_t *name, ngx_dir_t 
     }
 
     dir->valid_info = 0;
-#if (NGX_HAVE_D_TYPE)
-    dir->valid_type = 1;
-#else
-    dir->valid_type = 0;
-#endif
 
     return NGX_OK;
 }
@@ -267,6 +262,9 @@ ngx_read_dir(ngx_dir_t *dir)
     if (dir->de) {
 #if (NGX_HAVE_D_TYPE)
         dir->type = dir->de->d_type;
+        dir->valid_type = dir->type ? 1 : 0;
+#else
+        dir->valid_type = 0;
 #endif
         return NGX_OK;
     }
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -200,10 +200,25 @@ ngx_int_t ngx_read_dir(ngx_dir_t *dir);
 
 #if (NGX_HAVE_D_TYPE)
 
+#if (NGX_LINUX)
+
+/* XFS on Linux does not set dirent.d_type */
+
+#define ngx_de_is_dir(dir)                                                   \
+    (((dir)->type) ? ((dir)->type == DT_DIR) : (S_ISDIR((dir)->info.st_mode)))
+#define ngx_de_is_file(dir)                                                  \
+    (((dir)->type) ? ((dir)->type == DT_REG) : (S_ISREG((dir)->info.st_mode)))
+#define ngx_de_is_link(dir)                                                  \
+    (((dir)->type) ? ((dir)->type == DT_LINK) : (S_ISLNK((dir)->info.st_mode)))
+
+#else
+
 #define ngx_de_is_dir(dir)       ((dir)->type == DT_DIR)
 #define ngx_de_is_file(dir)      ((dir)->type == DT_REG)
 #define ngx_de_is_link(dir)      ((dir)->type == DT_LINK)
 
+#endif /* NGX_LINUX */
+
 #else
 
 #define ngx_de_is_dir(dir)       (S_ISDIR((dir)->info.st_mode))