]> granicus.if.org Git - apache/commitdiff
Mr. Trawick was dead on, and this revealed a much bigger bug.
authorWilliam A. Rowe Jr <wrowe@apache.org>
Fri, 1 Nov 2002 20:49:13 +0000 (20:49 +0000)
committerWilliam A. Rowe Jr <wrowe@apache.org>
Fri, 1 Nov 2002 20:49:13 +0000 (20:49 +0000)
  Factor out the opts/override merging (since we do it three times)
  and eliminate all the nasty goto's.  This bug likely caused all
  sorts of dir_walk configuration issues including htaccess issues.
  Also add a few more docs where things aren't so obvious.

PR: 14147

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@97380 13f79535-47bb-0310-9956-ffa450edef68

server/request.c

index b27cf724bd8624a71f7b1a1fda483c674ffb4531..38baf7484167c10e33c546e755612f6f7b218a64 100644 (file)
@@ -458,6 +458,51 @@ static int resolve_symlink(char *d, apr_finfo_t *lfi, int opts, apr_pool_t *p)
 }
 
 
+/*
+ * As we walk the directory configuration, the merged config won't
+ * be 'rooted' to a specific vhost until the very end of the merge.
+ *
+ * We need a very fast mini-merge to a real, vhost-rooted merge
+ * of core.opts and core.override, the only options tested within
+ * directory_walk itself.
+ *
+ * See core.c::merge_core_dir_configs() for explanation.
+ */
+
+typedef struct core_opts_t {
+        allow_options_t opts;
+        allow_options_t add;
+        allow_options_t remove;
+        overrides_t override;
+} core_opts_t;
+
+void core_opts_merge(const ap_conf_vector_t *sec, core_opts_t *opts)
+{
+    core_dir_config *this_dir = ap_get_module_config(sec, &core_module);
+
+    if (!this_dir) {
+        return;
+    }
+
+    if (this_dir->opts & OPT_UNSET) {
+        opts->add = (opts->add & ~this_dir->opts_remove)
+                   | this_dir->opts_add;
+        opts->remove = (opts->remove & ~this_dir->opts_add)
+                      | this_dir->opts_remove;
+        opts->opts = (opts->opts & ~opts->remove) | opts->add;
+    }
+    else {
+        opts->opts = this_dir->opts;
+        opts->add = this_dir->opts_add;
+        opts->remove = this_dir->opts_remove;
+    }
+
+    if (!(this_dir->override & OR_UNSET)) {
+        opts->override = this_dir->override;
+    }
+}
+
+
 /*****************************************************************
  *
  * Getting and checking directory configuration.  Also checks the
@@ -593,11 +638,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
         int matches = cache->walked->nelts;
         walk_walked_t *last_walk = (walk_walked_t*)cache->walked->elts;
         core_dir_config *this_dir;
-        allow_options_t opts;
-        allow_options_t opts_add;
-        allow_options_t opts_remove;
-        overrides_t override;
-
+        core_opts_t opts;
         apr_finfo_t thisinfo;
         char *save_path_info;
         apr_size_t buflen;
@@ -619,10 +660,10 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
          * accumulate opts and override as we merge, from the globals.
          */
         this_dir = ap_get_module_config(r->per_dir_config, &core_module);
-        opts = this_dir->opts;
-        opts_add = this_dir->opts_add;
-        opts_remove = this_dir->opts_remove;
-        override = this_dir->override;
+        opts.opts = this_dir->opts;
+        opts.add = this_dir->opts_add;
+        opts.remove = this_dir->opts_remove;
+        opts.override = this_dir->override;
 
         /* Set aside path_info to merge back onto path_info later.
          * If r->filename is a directory, we must remerge the path_info,
@@ -725,6 +766,8 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
          * seg keeps track of which segment we've copied.
          * sec_idx keeps track of which section we're on, since sections are
          *     ordered by number of segments. See core_reorder_directories
+         * startseg tells us how many segments describe the root path
+         *     e.g. the complete path "//host/foo/" to a UNC share (4)
          */
         startseg = seg = ap_count_dirs(r->filename);
         sec_idx = 0;
@@ -779,6 +822,12 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
                     continue;
                 }
 
+                /* If we haven't continue'd above, we have a match.
+                 *
+                 * Calculate our full-context core opts & override.
+                 */
+                core_opts_merge(sec_ent[sec_idx], &opts);
+
                 /* If we merged this same section last time, reuse it
                  */
                 if (matches) {
@@ -786,7 +835,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
                         now_merged = last_walk->merged;
                         ++last_walk;
                         --matches;
-                        goto minimerge;
+                        continue;
                     }
 
                     /* We fell out of sync.  This is our own copy of walked,
@@ -808,124 +857,78 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
                 last_walk = (walk_walked_t*)apr_array_push(cache->walked);
                 last_walk->matched = sec_ent[sec_idx];
                 last_walk->merged = now_merged;
-
-                /* Do a mini-merge to our globally-based running calculations of
-                 * core_dir->override and core_dir->opts, since now_merged
-                 * never considered the global config.  Of course, if there is
-                 * no core config at this level, continue without a thought.
-                 * See core.c::merge_core_dir_configs() for explanation.
-                 */
-minimerge:
-                this_dir = ap_get_module_config(sec_ent[sec_idx], &core_module);
-
-                if (!this_dir) {
-                    continue;
-                }
-
-                if (this_dir->opts & OPT_UNSET) {
-                    opts_add = (opts_add & ~this_dir->opts_remove)
-                               | this_dir->opts_add;
-                    opts_remove = (opts_remove & ~this_dir->opts_add)
-                                  | this_dir->opts_remove;
-                    opts = (opts & ~opts_remove) | opts_add;
-                }
-                else {
-                    opts = this_dir->opts;
-                    opts_add = this_dir->opts_add;
-                    opts_remove = this_dir->opts_remove;
-                }
-
-                if (!(this_dir->override & OR_UNSET)) {
-                    override = this_dir->override;
-                }
             }
 
             /* If .htaccess files are enabled, check for one, provided we
              * have reached a real path.
              */
-            if (seg >= startseg && override) {
+            do {  /* Not really a loop, just a break'able code block */
+
                 ap_conf_vector_t *htaccess_conf = NULL;
 
-                res = ap_parse_htaccess(&htaccess_conf, r, override,
+                /* No htaccess in an incomplete root path, 
+                 * nor if it's disabled 
+                 */
+                if (seg < startseg || !opts.override) {
+                    break;
+                }
+
+                res = ap_parse_htaccess(&htaccess_conf, r, opts.override,
                                         apr_pstrdup(r->pool, r->filename),
                                         sconf->access_name);
                 if (res) {
                     return res;
                 }
 
-                if (htaccess_conf) {
+                if (!htaccess_conf) {
+                    break;
+                }
 
-                    /* If we merged this same htaccess last time, reuse it...
-                     * this wouldn't work except that we cache the htaccess
-                     * sections for the lifetime of the request, so we match
-                     * the same conf.  Good planning (no, pure luck ;)
-                     */
-                    if (matches) {
-                        if (last_walk->matched == htaccess_conf) {
-                            now_merged = last_walk->merged;
-                            ++last_walk;
-                            --matches;
-                            goto minimerge2;
-                        }
-
-                        /* We fell out of sync.  This is our own copy of walked,
-                         * so truncate the remaining matches and reset
-                         * remaining.
-                         */
-                        cache->walked->nelts -= matches;
-                        matches = 0;
-                    }
+                /* If we are still here, we found our htaccess.
+                 *
+                 * Calculate our full-context core opts & override.
+                 */
+                core_opts_merge(htaccess_conf, &opts);
 
-                    if (now_merged) {
-                        now_merged = ap_merge_per_dir_configs(r->pool,
-                                                              now_merged,
-                                                              htaccess_conf);
-                    }
-                    else {
-                        now_merged = htaccess_conf;
+                /* If we merged this same htaccess last time, reuse it...
+                 * this wouldn't work except that we cache the htaccess
+                 * sections for the lifetime of the request, so we match
+                 * the same conf.  Good planning (no, pure luck ;)
+                 */
+                if (matches) {
+                    if (last_walk->matched == htaccess_conf) {
+                        now_merged = last_walk->merged;
+                        ++last_walk;
+                        --matches;
+                        break;
                     }
 
-                    last_walk = (walk_walked_t*)apr_array_push(cache->walked);
-                    last_walk->matched = htaccess_conf;
-                    last_walk->merged = now_merged;
-
-                    /* Do a mini-merge to our globally-based running
-                     * calculations of core_dir->override and core_dir->opts,
-                     * since now_merged never considered the global config.
-                     * Of course, if there is no core config at this level,
-                     * continue without a thought.
-                     * See core.c::merge_core_dir_configs() for explanation.
+                    /* We fell out of sync.  This is our own copy of walked,
+                     * so truncate the remaining matches and reset
+                     * remaining.
                      */
-minimerge2:
-                    this_dir = ap_get_module_config(htaccess_conf,
-                                                    &core_module);
-
-                    if (this_dir) {
-                        if (this_dir->opts & OPT_UNSET) {
-                            opts_add = (opts_add & ~this_dir->opts_remove)
-                                       | this_dir->opts_add;
-                            opts_remove = (opts_remove & ~this_dir->opts_add)
-                                          | this_dir->opts_remove;
-                            opts = (opts & ~opts_remove) | opts_add;
-                        }
-                        else {
-                            opts = this_dir->opts;
-                            opts_add = this_dir->opts_add;
-                            opts_remove = this_dir->opts_remove;
-                        }
-
-                        if (!(this_dir->override & OR_UNSET)) {
-                            override = this_dir->override;
-                        }
-                    }
+                    cache->walked->nelts -= matches;
+                    matches = 0;
                 }
-            }
+
+                if (now_merged) {
+                    now_merged = ap_merge_per_dir_configs(r->pool,
+                                                          now_merged,
+                                                          htaccess_conf);
+                }
+                else {
+                    now_merged = htaccess_conf;
+                }
+
+                last_walk = (walk_walked_t*)apr_array_push(cache->walked);
+                last_walk->matched = htaccess_conf;
+                last_walk->merged = now_merged;
+
+            } while (0); /* Only one htaccess, not a real loop */
 
             /* That temporary trailing slash was useful, now drop it.
              */
             if (temp_slash) {
-                temp_slash = 0;
-                AP_DEBUG_ASSERT(r->filename[filename_len-1] == '/');
                 r->filename[--filename_len] = '\0';
             }
 
@@ -976,7 +979,7 @@ minimerge2:
 #ifdef CASE_BLIND_FILESYSTEM
                 && (filename_len <= canonical_len)
 #endif
-                && ((opts & (OPT_SYM_OWNER | OPT_SYM_LINKS)) == OPT_SYM_LINKS))
+                && ((opts.opts & (OPT_SYM_OWNER | OPT_SYM_LINKS)) == OPT_SYM_LINKS))
             {
 
                 thisinfo.filetype = APR_DIR;
@@ -1036,7 +1039,7 @@ minimerge2:
                 /* Is this a possibly acceptable symlink?
                  */
                 if ((res = resolve_symlink(r->filename, &thisinfo,
-                                           opts, r->pool)) != OK) {
+                                           opts.opts, r->pool)) != OK) {
                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                                   "Symbolic link not allowed: %s",
                                   r->filename);
@@ -1098,6 +1101,12 @@ minimerge2:
                 continue;
             }
 
+            /* If we haven't already continue'd above, we have a match.
+             *
+             * Calculate our full-context core opts & override.
+             */
+            core_opts_merge(sec_ent[sec_idx], &opts);
+
             /* If we merged this same section last time, reuse it
              */
             if (matches) {
@@ -1105,7 +1114,7 @@ minimerge2:
                     now_merged = last_walk->merged;
                     ++last_walk;
                     --matches;
-                    goto minimerge;
+                    continue;
                 }
 
                 /* We fell out of sync.  This is our own copy of walked,