You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by mi...@apache.org on 2018/11/25 21:19:13 UTC
svn commit: r1847431 - /httpd/httpd/patches/2.4.x/httpd-ap_dir_fnmatch.patch
Author: minfrin
Date: Sun Nov 25 21:19:13 2018
New Revision: 1847431
URL: http://svn.apache.org/viewvc?rev=1847431&view=rev
Log:
Add httpd v2.4 backport of r1847430.
Added:
httpd/httpd/patches/2.4.x/httpd-ap_dir_fnmatch.patch
Added: httpd/httpd/patches/2.4.x/httpd-ap_dir_fnmatch.patch
URL: http://svn.apache.org/viewvc/httpd/httpd/patches/2.4.x/httpd-ap_dir_fnmatch.patch?rev=1847431&view=auto
==============================================================================
--- httpd/httpd/patches/2.4.x/httpd-ap_dir_fnmatch.patch (added)
+++ httpd/httpd/patches/2.4.x/httpd-ap_dir_fnmatch.patch Sun Nov 25 21:19:13 2018
@@ -0,0 +1,653 @@
+Index: CHANGES
+===================================================================
+--- CHANGES (revision 1847315)
++++ CHANGES (working copy)
+@@ -1,6 +1,10 @@
+ -*- coding: utf-8 -*-
+ Changes with Apache 2.4.38
+
++ *) core: Split out the ability to parse wildcard files and directories
++ from the Include/IncludeOptional directives into a generic set of
++ functions ap_dir_nofnmatch() and ap_dir_fnmatch(). [Graham Leggett]
++
+ *) mod_setenvif: We can have expressions that become true if a regex pattern
+ in the expression does NOT match. In this case val is NULL
+ and we should just set the value for the environment variable
+Index: include/ap_mmn.h
+===================================================================
+--- include/ap_mmn.h (revision 1847315)
++++ include/ap_mmn.h (working copy)
+@@ -523,6 +523,7 @@
+ * 20120211.82 (2.4.35-dev) Add optional function declaration for
+ * ap_proxy_balancer_get_best_worker to mod_proxy.h.
+ * 20120211.83 (2.4.35-dev) Add client64 field to worker_score struct
++ * 20120211.84 (2.4.35-dev) Add ap_dir_nofnmatch() and ap_dir_fnmatch().
+ *
+ */
+
+Index: include/http_config.h
+===================================================================
+--- include/http_config.h (revision 1847315)
++++ include/http_config.h (working copy)
+@@ -928,6 +928,21 @@
+ ap_conf_vector_t *section_vector);
+
+ /**
++ * Convenience function to create a ap_dir_match_t structure from a cmd_parms.
++ *
++ * @param cmd The command.
++ * @param flags Flags to indicate whether optional or recursive.
++ * @param cb Callback for each file found that matches the wildcard. Return NULL on
++ * success, an error string on error.
++ * @param ctx Context for the callback.
++ * @return Structure ap_dir_match_t with fields populated, allocated from the
++ * cmd->temp_pool.
++ */
++AP_DECLARE(ap_dir_match_t *)ap_dir_cfgmatch(cmd_parms *cmd, int flags,
++ const char *(*cb)(ap_dir_match_t *w, const char *fname), void *ctx)
++ __attribute__((nonnull(1,3)));
++
++/**
+ * @defgroup ap_check_cmd_context Check command context
+ * @{
+ */
+Index: include/httpd.h
+===================================================================
+--- include/httpd.h (revision 1847315)
++++ include/httpd.h (working copy)
+@@ -2397,6 +2397,92 @@
+ */
+ AP_DECLARE(int) ap_cstr_casecmpn(const char *s1, const char *s2, apr_size_t n);
+
++/**
++ * Default flags for apr_dir_*().
++ */
++#define AP_DIR_FLAG_NONE 0
++
++/**
++ * If set, wildcards that match no files or directories will be ignored, otherwise
++ * an error is triggered.
++ */
++#define AP_DIR_FLAG_OPTIONAL 1
++
++/**
++ * If set, and the wildcard resolves to a directory, recursively find all files
++ * below that directory, otherwise return the directory.
++ */
++#define AP_DIR_FLAG_RECURSIVE 2
++
++/**
++ * Structure to provide the state of a directory match.
++ */
++typedef struct ap_dir_match_t ap_dir_match_t;
++
++/**
++ * Concrete structure to provide the state of a directory match.
++ */
++struct ap_dir_match_t {
++ /** Pool to use for allocating the result */
++ apr_pool_t *p;
++ /** Temporary pool used for directory traversal */
++ apr_pool_t *ptemp;
++ /** Prefix for log messages */
++ const char *prefix;
++ /** Callback for each file found that matches the wildcard. Return NULL on success, an error string on error. */
++ const char *(*cb)(ap_dir_match_t *w, const char *fname);
++ /** Context for the callback */
++ void *ctx;
++ /** Flags to indicate whether optional or recursive */
++ int flags;
++ /** Recursion depth safety check */
++ unsigned int depth;
++};
++
++/**
++ * Search for files given a non wildcard filename with non native separators.
++ *
++ * If the provided filename points at a file, the callback within ap_dir_match_t is
++ * triggered for that file, and this function returns the result of the callback.
++ *
++ * If the provided filename points at a directory, and recursive within ap_dir_match_t
++ * is true, the callback will be triggered for every file found recursively beneath
++ * that directory, otherwise the callback is triggered once for the directory itself.
++ * This function returns the result of the callback.
++ *
++ * If the provided path points to neither a file nor a directory, and optional within
++ * ap_dir_match_t is true, this function returns NULL. If optional within ap_dir_match_t
++ * is false, this function will return an error string indicating that the path does not
++ * exist.
++ *
++ * @param w Directory match structure containing callback and context.
++ * @param fname The name of the file or directory, with non native separators.
++ * @return NULL on success, or a string describing the error.
++ */
++AP_DECLARE(const char *)ap_dir_nofnmatch(ap_dir_match_t *w, const char *fname)
++ __attribute__((nonnull(1,2)));
++
++/**
++ * Search for files given a wildcard filename with non native separators.
++ *
++ * If the filename contains a wildcard, all files and directories that match the wildcard
++ * will be returned.
++ *
++ * ap_dir_nofnmatch() is called for each directory and file found, and the callback
++ * within ap_dir_match_t triggered as described above.
++ *
++ * Wildcards may appear in both directory and file components in the path, and
++ * wildcards may appear more than once.
++ *
++ * @param w Directory match structure containing callback and context.
++ * @param path Path prefix for search, with non native separators and no wildcards.
++ * @param fname The name of the file or directory, with non native separators and
++ * optional wildcards.
++ * @return NULL on success, or a string describing the error.
++ */
++AP_DECLARE(const char *)ap_dir_fnmatch(ap_dir_match_t *w, const char *path,
++ const char *fname) __attribute__((nonnull(1,3)));
++
+ #ifdef __cplusplus
+ }
+ #endif
+Index: server/config.c
+===================================================================
+--- server/config.c (revision 1847315)
++++ server/config.c (working copy)
+@@ -1793,18 +1793,6 @@
+ return NULL;
+ }
+
+-typedef struct {
+- const char *fname;
+-} fnames;
+-
+-static int fname_alphasort(const void *fn1, const void *fn2)
+-{
+- const fnames *f1 = fn1;
+- const fnames *f2 = fn2;
+-
+- return strcmp(f1->fname,f2->fname);
+-}
+-
+ /**
+ * Used by -D DUMP_INCLUDES to output the config file "tree".
+ */
+@@ -1897,200 +1885,15 @@
+ return NULL;
+ }
+
+-static const char *process_resource_config_nofnmatch(server_rec *s,
+- const char *fname,
+- ap_directive_t **conftree,
+- apr_pool_t *p,
+- apr_pool_t *ptemp,
+- unsigned depth,
+- int optional)
+-{
+- const char *error;
+- apr_status_t rv;
++typedef struct {
++ server_rec *s;
++ ap_directive_t **conftree;
++} configs;
+
+- if (ap_is_directory(ptemp, fname)) {
+- apr_dir_t *dirp;
+- apr_finfo_t dirent;
+- int current;
+- apr_array_header_t *candidates = NULL;
+- fnames *fnew;
+- char *path = apr_pstrdup(ptemp, fname);
+-
+- if (++depth > AP_MAX_INCLUDE_DIR_DEPTH) {
+- return apr_psprintf(p, "Directory %s exceeds the maximum include "
+- "directory nesting level of %u. You have "
+- "probably a recursion somewhere.", path,
+- AP_MAX_INCLUDE_DIR_DEPTH);
+- }
+-
+- /*
+- * first course of business is to grok all the directory
+- * entries here and store 'em away. Recall we need full pathnames
+- * for this.
+- */
+- rv = apr_dir_open(&dirp, path, ptemp);
+- if (rv != APR_SUCCESS) {
+- return apr_psprintf(p, "Could not open config directory %s: %pm",
+- path, &rv);
+- }
+-
+- candidates = apr_array_make(ptemp, 1, sizeof(fnames));
+- while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) {
+- /* strip out '.' and '..' */
+- if (strcmp(dirent.name, ".")
+- && strcmp(dirent.name, "..")) {
+- fnew = (fnames *) apr_array_push(candidates);
+- fnew->fname = ap_make_full_path(ptemp, path, dirent.name);
+- }
+- }
+-
+- apr_dir_close(dirp);
+- if (candidates->nelts != 0) {
+- qsort((void *) candidates->elts, candidates->nelts,
+- sizeof(fnames), fname_alphasort);
+-
+- /*
+- * Now recurse these... we handle errors and subdirectories
+- * via the recursion, which is nice
+- */
+- for (current = 0; current < candidates->nelts; ++current) {
+- fnew = &((fnames *) candidates->elts)[current];
+- error = process_resource_config_nofnmatch(s, fnew->fname,
+- conftree, p, ptemp,
+- depth, optional);
+- if (error) {
+- return error;
+- }
+- }
+- }
+-
+- return NULL;
+- }
+- else if (optional) {
+- /* If the optinal flag is set (like for IncludeOptional) we can
+- * tolerate that no file or directory is present and bail out.
+- */
+- apr_finfo_t finfo;
+- if (apr_stat(&finfo, fname, APR_FINFO_TYPE, ptemp) != APR_SUCCESS
+- || finfo.filetype == APR_NOFILE)
+- return NULL;
+- }
+-
+- return ap_process_resource_config(s, fname, conftree, p, ptemp);
+-}
+-
+-static const char *process_resource_config_fnmatch(server_rec *s,
+- const char *path,
+- const char *fname,
+- ap_directive_t **conftree,
+- apr_pool_t *p,
+- apr_pool_t *ptemp,
+- unsigned depth,
+- int optional)
++static const char *process_resource_config_cb(ap_dir_match_t *w, const char *fname)
+ {
+- const char *rest;
+- apr_status_t rv;
+- apr_dir_t *dirp;
+- apr_finfo_t dirent;
+- apr_array_header_t *candidates = NULL;
+- fnames *fnew;
+- int current;
+-
+- /* find the first part of the filename */
+- rest = ap_strchr_c(fname, '/');
+- if (rest) {
+- fname = apr_pstrmemdup(ptemp, fname, rest - fname);
+- rest++;
+- }
+-
+- /* optimisation - if the filename isn't a wildcard, process it directly */
+- if (!apr_fnmatch_test(fname)) {
+- path = ap_make_full_path(ptemp, path, fname);
+- if (!rest) {
+- return process_resource_config_nofnmatch(s, path,
+- conftree, p,
+- ptemp, 0, optional);
+- }
+- else {
+- return process_resource_config_fnmatch(s, path, rest,
+- conftree, p,
+- ptemp, 0, optional);
+- }
+- }
+-
+- /*
+- * first course of business is to grok all the directory
+- * entries here and store 'em away. Recall we need full pathnames
+- * for this.
+- */
+- rv = apr_dir_open(&dirp, path, ptemp);
+- if (rv != APR_SUCCESS) {
+- /* If the directory doesn't exist and the optional flag is set
+- * there is no need to return an error.
+- */
+- if (rv == APR_ENOENT && optional) {
+- return NULL;
+- }
+- return apr_psprintf(p, "Could not open config directory %s: %pm",
+- path, &rv);
+- }
+-
+- candidates = apr_array_make(ptemp, 1, sizeof(fnames));
+- while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp) == APR_SUCCESS) {
+- /* strip out '.' and '..' */
+- if (strcmp(dirent.name, ".")
+- && strcmp(dirent.name, "..")
+- && (apr_fnmatch(fname, dirent.name,
+- APR_FNM_PERIOD) == APR_SUCCESS)) {
+- const char *full_path = ap_make_full_path(ptemp, path, dirent.name);
+- /* If matching internal to path, and we happen to match something
+- * other than a directory, skip it
+- */
+- if (rest && (dirent.filetype != APR_DIR)) {
+- continue;
+- }
+- fnew = (fnames *) apr_array_push(candidates);
+- fnew->fname = full_path;
+- }
+- }
+-
+- apr_dir_close(dirp);
+- if (candidates->nelts != 0) {
+- const char *error;
+-
+- qsort((void *) candidates->elts, candidates->nelts,
+- sizeof(fnames), fname_alphasort);
+-
+- /*
+- * Now recurse these... we handle errors and subdirectories
+- * via the recursion, which is nice
+- */
+- for (current = 0; current < candidates->nelts; ++current) {
+- fnew = &((fnames *) candidates->elts)[current];
+- if (!rest) {
+- error = process_resource_config_nofnmatch(s, fnew->fname,
+- conftree, p,
+- ptemp, 0, optional);
+- }
+- else {
+- error = process_resource_config_fnmatch(s, fnew->fname, rest,
+- conftree, p,
+- ptemp, 0, optional);
+- }
+- if (error) {
+- return error;
+- }
+- }
+- }
+- else {
+-
+- if (!optional) {
+- return apr_psprintf(p, "No matches for the wildcard '%s' in '%s', failing "
+- "(use IncludeOptional if required)", fname, path);
+- }
+- }
+-
+- return NULL;
++ configs *cfgs = w->ctx;
++ return ap_process_resource_config(cfgs->s, fname, cfgs->conftree, w->p, w->ptemp);
+ }
+
+ AP_DECLARE(const char *) ap_process_fnmatch_configs(server_rec *s,
+@@ -2100,9 +1903,19 @@
+ apr_pool_t *ptemp,
+ int optional)
+ {
+- /* XXX: lstat() won't work on the wildcard pattern...
+- */
++ configs cfgs;
++ ap_dir_match_t w;
+
++ cfgs.s = s;
++ cfgs.conftree = conftree;
++
++ w.prefix = "Include/IncludeOptional: ";
++ w.p = p;
++ w.ptemp = ptemp;
++ w.flags = (optional ? AP_DIR_FLAG_OPTIONAL : AP_DIR_FLAG_NONE) | AP_DIR_FLAG_RECURSIVE;
++ w.cb = process_resource_config_cb;
++ w.ctx = &cfgs;
++
+ /* don't require conf/httpd.conf if we have a -C or -c switch */
+ if ((ap_server_pre_read_config->nelts
+ || ap_server_post_read_config->nelts)
+@@ -2114,7 +1927,7 @@
+ }
+
+ if (!apr_fnmatch_test(fname)) {
+- return process_resource_config_nofnmatch(s, fname, conftree, p, ptemp, 0, optional);
++ return ap_dir_nofnmatch(&w, fname);
+ }
+ else {
+ apr_status_t status;
+@@ -2132,8 +1945,7 @@
+ }
+
+ /* walk the filepath */
+- return process_resource_config_fnmatch(s, rootpath, filepath, conftree, p, ptemp,
+- 0, optional);
++ return ap_dir_fnmatch(&w, rootpath, filepath);
+ }
+ }
+
+Index: server/util.c
+===================================================================
+--- server/util.c (revision 1847315)
++++ server/util.c (working copy)
+@@ -47,6 +47,7 @@
+
+ #include "ap_config.h"
+ #include "apr_base64.h"
++#include "apr_fnmatch.h"
+ #include "httpd.h"
+ #include "http_main.h"
+ #include "http_log.h"
+@@ -96,6 +97,11 @@
+ #undef APLOG_MODULE_INDEX
+ #define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
++/* maximum nesting level for config directories */
++#ifndef AP_MAX_FNMATCH_DIR_DEPTH
++#define AP_MAX_FNMATCH_DIR_DEPTH (128)
++#endif
++
+ /*
+ * Examine a field value (such as a media-/content-type) string and return
+ * it sans any parameters; e.g., strip off any ';charset=foo' and the like.
+@@ -3307,3 +3313,206 @@
+ return 0;
+ }
+
++typedef struct {
++ const char *fname;
++} fnames;
++
++static int fname_alphasort(const void *fn1, const void *fn2)
++{
++ const fnames *f1 = fn1;
++ const fnames *f2 = fn2;
++
++ return strcmp(f1->fname,f2->fname);
++}
++
++AP_DECLARE(ap_dir_match_t *)ap_dir_cfgmatch(cmd_parms *cmd, int flags,
++ const char *(*cb)(ap_dir_match_t *w, const char *fname), void *ctx)
++{
++ ap_dir_match_t *w = apr_palloc(cmd->temp_pool, sizeof(cmd_parms));
++
++ w->prefix = apr_pstrcat(cmd->pool, cmd->cmd->name, ": ", NULL);
++ w->p = cmd->pool;
++ w->ptemp = cmd->temp_pool;
++ w->flags = flags;
++ w->cb = cb;
++ w->ctx = ctx;
++ w->depth = 0;
++
++ return w;
++}
++
++AP_DECLARE(const char *)ap_dir_nofnmatch(ap_dir_match_t *w, const char *fname)
++{
++ const char *error;
++ apr_status_t rv;
++
++ if ((w->flags & AP_DIR_FLAG_RECURSIVE) && ap_is_directory(w->ptemp, fname)) {
++ apr_dir_t *dirp;
++ apr_finfo_t dirent;
++ int current;
++ apr_array_header_t *candidates = NULL;
++ fnames *fnew;
++ char *path = apr_pstrdup(w->ptemp, fname);
++
++ if (++w->depth > AP_MAX_FNMATCH_DIR_DEPTH) {
++ return apr_psprintf(w->p, "%sDirectory '%s' exceeds the maximum include "
++ "directory nesting level of %u. You have "
++ "probably a recursion somewhere.", w->prefix ? w->prefix : "", path,
++ AP_MAX_FNMATCH_DIR_DEPTH);
++ }
++
++ /*
++ * first course of business is to grok all the directory
++ * entries here and store 'em away. Recall we need full pathnames
++ * for this.
++ */
++ rv = apr_dir_open(&dirp, path, w->ptemp);
++ if (rv != APR_SUCCESS) {
++ return apr_psprintf(w->p, "%sCould not open directory %s: %pm",
++ w->prefix ? w->prefix : "", path, &rv);
++ }
++
++ candidates = apr_array_make(w->ptemp, 1, sizeof(fnames));
++ while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) {
++ /* strip out '.' and '..' */
++ if (strcmp(dirent.name, ".")
++ && strcmp(dirent.name, "..")) {
++ fnew = (fnames *) apr_array_push(candidates);
++ fnew->fname = ap_make_full_path(w->ptemp, path, dirent.name);
++ }
++ }
++
++ apr_dir_close(dirp);
++ if (candidates->nelts != 0) {
++ qsort((void *) candidates->elts, candidates->nelts,
++ sizeof(fnames), fname_alphasort);
++
++ /*
++ * Now recurse these... we handle errors and subdirectories
++ * via the recursion, which is nice
++ */
++ for (current = 0; current < candidates->nelts; ++current) {
++ fnew = &((fnames *) candidates->elts)[current];
++ error = ap_dir_nofnmatch(w, fnew->fname);
++ if (error) {
++ return error;
++ }
++ }
++ }
++
++ w->depth--;
++
++ return NULL;
++ }
++ else if (w->flags & AP_DIR_FLAG_OPTIONAL) {
++ /* If the optional flag is set (like for IncludeOptional) we can
++ * tolerate that no file or directory is present and bail out.
++ */
++ apr_finfo_t finfo;
++ if (apr_stat(&finfo, fname, APR_FINFO_TYPE, w->ptemp) != APR_SUCCESS
++ || finfo.filetype == APR_NOFILE)
++ return NULL;
++ }
++
++ return w->cb(w, fname);
++}
++
++AP_DECLARE(const char *)ap_dir_fnmatch(ap_dir_match_t *w, const char *path,
++ const char *fname)
++{
++ const char *rest;
++ apr_status_t rv;
++ apr_dir_t *dirp;
++ apr_finfo_t dirent;
++ apr_array_header_t *candidates = NULL;
++ fnames *fnew;
++ int current;
++
++ /* find the first part of the filename */
++ rest = ap_strchr_c(fname, '/');
++ if (rest) {
++ fname = apr_pstrmemdup(w->ptemp, fname, rest - fname);
++ rest++;
++ }
++
++ /* optimisation - if the filename isn't a wildcard, process it directly */
++ if (!apr_fnmatch_test(fname)) {
++ path = path ? ap_make_full_path(w->ptemp, path, fname) : fname;
++ if (!rest) {
++ return ap_dir_nofnmatch(w, path);
++ }
++ else {
++ return ap_dir_fnmatch(w, path, rest);
++ }
++ }
++
++ /*
++ * first course of business is to grok all the directory
++ * entries here and store 'em away. Recall we need full pathnames
++ * for this.
++ */
++ rv = apr_dir_open(&dirp, path, w->ptemp);
++ if (rv != APR_SUCCESS) {
++ /* If the directory doesn't exist and the optional flag is set
++ * there is no need to return an error.
++ */
++ if (rv == APR_ENOENT && (w->flags & AP_DIR_FLAG_OPTIONAL)) {
++ return NULL;
++ }
++ return apr_psprintf(w->p, "%sCould not open directory %s: %pm",
++ w->prefix ? w->prefix : "", path, &rv);
++ }
++
++ candidates = apr_array_make(w->ptemp, 1, sizeof(fnames));
++ while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp) == APR_SUCCESS) {
++ /* strip out '.' and '..' */
++ if (strcmp(dirent.name, ".")
++ && strcmp(dirent.name, "..")
++ && (apr_fnmatch(fname, dirent.name,
++ APR_FNM_PERIOD) == APR_SUCCESS)) {
++ const char *full_path = ap_make_full_path(w->ptemp, path, dirent.name);
++ /* If matching internal to path, and we happen to match something
++ * other than a directory, skip it
++ */
++ if (rest && (dirent.filetype != APR_DIR)) {
++ continue;
++ }
++ fnew = (fnames *) apr_array_push(candidates);
++ fnew->fname = full_path;
++ }
++ }
++
++ apr_dir_close(dirp);
++ if (candidates->nelts != 0) {
++ const char *error;
++
++ qsort((void *) candidates->elts, candidates->nelts,
++ sizeof(fnames), fname_alphasort);
++
++ /*
++ * Now recurse these... we handle errors and subdirectories
++ * via the recursion, which is nice
++ */
++ for (current = 0; current < candidates->nelts; ++current) {
++ fnew = &((fnames *) candidates->elts)[current];
++ if (!rest) {
++ error = ap_dir_nofnmatch(w, fnew->fname);
++ }
++ else {
++ error = ap_dir_fnmatch(w, fnew->fname, rest);
++ }
++ if (error) {
++ return error;
++ }
++ }
++ }
++ else {
++
++ if (!(w->flags & AP_DIR_FLAG_OPTIONAL)) {
++ return apr_psprintf(w->p, "%sNo matches for the wildcard '%s' in '%s', failing",
++ w->prefix ? w->prefix : "", fname, path);
++ }
++ }
++
++ return NULL;
++}