Commit e75427fd authored by Graham Leggett's avatar Graham Leggett
Browse files

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().


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1847430 13f79535-47bb-0310-9956-ffa450edef68
parent 4d27feaf
Loading
Loading
Loading
Loading
+4 −0
Changes for CHANGES: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
                                                         -*- coding: utf-8 -*-
Changes with Apache 2.5.1

  *) 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_dav: Fix an unlikely time-window where some incorrect data could be returned
     from a PROPFIND request [Ruediger Pluem]

+1 −0
Changes for include/ap_mmn.h: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -608,6 +608,7 @@
 *                         fields by struct ap_filter_private *priv
 * 20180906.1 (2.5.1-dev)  Don't export ap_filter_recycle() anymore
 * 20180906.2 (2.5.1-dev)  Add ap_state_dir_relative()
 * 20180906.3 (2.5.1-dev)  Add ap_dir_nofnmatch() and ap_dir_fnmatch().
 */

#define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
+15 −0
Changes for include/http_config.h: 15 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -938,6 +938,21 @@ AP_DECLARE(const char *) ap_walk_config(ap_directive_t *conftree,
                                        cmd_parms *parms,
                                        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
 * @{
+86 −0
Changes for include/httpd.h: 86 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -2559,6 +2559,92 @@ AP_DECLARE(int) ap_cstr_casecmp(const char *s1, const char *s2);
 */
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
+21 −209
Changes for server/config.c: 21 added lines, 209 removed lines.
Original line number Diff line number Diff line
@@ -1795,18 +1795,6 @@ static const char *process_command_config(server_rec *s,
    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".
 */
@@ -1902,200 +1890,15 @@ AP_DECLARE(const char *) ap_process_resource_config(server_rec *s,
    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;

    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);
}
typedef struct {
    server_rec *s;
    ap_directive_t **conftree;
} configs;

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,
@@ -2105,8 +1908,18 @@ AP_DECLARE(const char *) ap_process_fnmatch_configs(server_rec *s,
                                                    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
@@ -2119,7 +1932,7 @@ AP_DECLARE(const char *) ap_process_fnmatch_configs(server_rec *s,
    }

    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;
@@ -2137,8 +1950,7 @@ AP_DECLARE(const char *) ap_process_fnmatch_configs(server_rec *s,
        }

        /* walk the filepath */
        return process_resource_config_fnmatch(s, rootpath, filepath, conftree, p, ptemp,
                                               0, optional);
        return ap_dir_fnmatch(&w, rootpath, filepath);
    }
}

Loading