Previously, files included with the acl <..> -f <filename> construct would
open <filename> relative to the current directory. This would mean that

( cd /tmp ; haproxy -d -f /etc/haproxy/haprocy.cfg ) and
( cd /etc/haproxy ; haproxy -d -f /etc/haproxy/haprocy.cfg )

could give different results if the configuration file had
any relative pathnames in acl -f constructs.

Change this to always open included files relative to the
configuration file they are being included from.
---
 include/proto/acl.h |    8 ++++--
 src/acl.c           |   52 +++++++++++++++++++++++++++++++++++++++++---------
 src/cfgparse.c      |    2 +-
 3 files changed, 48 insertions(+), 14 deletions(-)

diff --git a/include/proto/acl.h b/include/proto/acl.h
index c35cee9..c5f412f 100644
--- a/include/proto/acl.h
+++ b/include/proto/acl.h
@@ -57,8 +57,9 @@ struct acl_keyword *find_acl_kw(const char *kw);
 /* Parse an ACL expression starting at <args>[0], and return it.
  * Right now, the only accepted syntax is :
  * <subject> [<value>...]
+ * The ACL is defined in the configuration file <configfile>
  */
-struct acl_expr *parse_acl_expr(const char **args);
+struct acl_expr *parse_acl_expr(const char *configfile, const char **args);
 
 /* Purge everything in the acl <acl>, then return <acl>. */
 struct acl *prune_acl(struct acl *acl);
@@ -66,10 +67,11 @@ struct acl *prune_acl(struct acl *acl);
 /* Parse an ACL with the name starting at <args>[0], and with a list of already
  * known ACLs in <acl>. If the ACL was not in the list, it will be added.
  * A pointer to that ACL is returned.
+ * The ACL is defined in the file <filename>.
  *
  * args syntax: <aclname> <acl_expr>
  */
-struct acl *parse_acl(const char **args, struct list *known_acl);
+struct acl *parse_acl(const char *filename, const char **args, struct list 
*known_acl);
 
 /* Purge everything in the acl_cond <cond>, then return <cond>. */
 struct acl_cond *prune_acl_cond(struct acl_cond *cond);
@@ -78,7 +80,7 @@ struct acl_cond *prune_acl_cond(struct acl_cond *cond);
  * known ACLs passed in <known_acl>. The new condition is returned (or NULL in
  * case of low memory). Supports multiple conditions separated by "or".
  */
-struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, int 
pol);
+struct acl_cond *parse_acl_cond(const char *configfile, const char **args, 
struct list *known_acl, int pol);
 
 /* Builds an ACL condition starting at the if/unless keyword. The complete
  * condition is returned. NULL is returned in case of error or if the first
diff --git a/src/acl.c b/src/acl.c
index 9d9a746..f85f0d0 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -1053,7 +1053,36 @@ static struct acl_expr *prune_acl_expr(struct acl_expr 
*expr)
        return expr;
 }
 
-static int acl_read_patterns_from_file(        struct acl_keyword *aclkw,
+/* Return the full pathname of <filename> relative to <basefile>.
+ * If there is any kind of problem, just return <filename>.
+ *
+ * <buffer> may be used to construct the result, and
+ * up to <buflen> bytes will be used. If the result does not fit,
+ * <filename> will be returned instead.
+ */
+static const char *combine_paths(const char *basefile, const char *filename, 
char *buffer, size_t buflen)
+{
+       const char *last_slash;
+       size_t baselen;
+
+       if (!basefile || filename[0] == '/')
+               return filename;
+
+       last_slash = strrchr(basefile, '/');
+       if (!last_slash)
+               return filename;
+
+       baselen = last_slash + 1 - basefile;
+       if (baselen + strlen(filename) + 1 > buflen)
+               return filename;
+
+       memcpy(buffer, basefile, baselen);
+       memcpy(buffer + baselen, filename, strlen(filename) + 1);
+       return buffer;
+}
+
+static int acl_read_patterns_from_file(        const char *configfile,
+                                       struct acl_keyword *aclkw,
                                        struct acl_expr *expr,
                                        const char *filename, int patflags)
 {
@@ -1063,8 +1092,9 @@ static int acl_read_patterns_from_file(   struct 
acl_keyword *aclkw,
        struct acl_pattern *pattern;
        int opaque;
        int ret = 0;
+       char buffer[MAXPATHLEN];
 
-       file = fopen(filename, "r");
+       file = fopen(combine_paths(configfile, filename, buffer, sizeof 
buffer), "r");
        if (!file)
                return 0;
 
@@ -1137,7 +1167,7 @@ static int acl_read_patterns_from_file(   struct 
acl_keyword *aclkw,
  * Right now, the only accepted syntax is :
  * <subject> [<value>...]
  */
-struct acl_expr *parse_acl_expr(const char **args)
+struct acl_expr *parse_acl_expr(const char *configfile, const char **args)
 {
        __label__ out_return, out_free_expr, out_free_pattern;
        struct acl_expr *expr;
@@ -1188,7 +1218,7 @@ struct acl_expr *parse_acl_expr(const char **args)
                if ((*args)[1] == 'i')
                        patflags |= ACL_PAT_F_IGNORE_CASE;
                else if ((*args)[1] == 'f') {
-                       if (!acl_read_patterns_from_file(aclkw, expr, args[1], 
patflags | ACL_PAT_F_FROM_FILE))
+                       if (!acl_read_patterns_from_file(configfile, aclkw, 
expr, args[1], patflags | ACL_PAT_F_FROM_FILE))
                                goto out_free_expr;
                        args++;
                }
@@ -1249,9 +1279,11 @@ struct acl *prune_acl(struct acl *acl) {
  * A pointer to that ACL is returned. If the ACL has an empty name, then it's
  * an anonymous one and it won't be merged with any other one.
  *
+ * The ACL is defined in the file <file> - required for parsing "-f" constructs
+ *
  * args syntax: <aclname> <acl_expr>
  */
-struct acl *parse_acl(const char **args, struct list *known_acl)
+struct acl *parse_acl(const char *file, const char **args, struct list 
*known_acl)
 {
        __label__ out_return, out_free_acl_expr, out_free_name;
        struct acl *cur_acl;
@@ -1261,7 +1293,7 @@ struct acl *parse_acl(const char **args, struct list 
*known_acl)
        if (**args && invalid_char(*args))
                goto out_return;
 
-       acl_expr = parse_acl_expr(args + 1);
+       acl_expr = parse_acl_expr(file, args + 1);
        if (!acl_expr)
                goto out_return;
 
@@ -1357,7 +1389,7 @@ struct acl *find_acl_default(const char *acl_name, struct 
list *known_acl)
        if (default_acl_list[index].name == NULL)
                return NULL;
 
-       acl_expr = parse_acl_expr((const char **)default_acl_list[index].expr);
+       acl_expr = parse_acl_expr(NULL, (const char 
**)default_acl_list[index].expr);
        if (!acl_expr)
                goto out_return;
 
@@ -1405,7 +1437,7 @@ struct acl_cond *prune_acl_cond(struct acl_cond *cond)
  * known ACLs passed in <known_acl>. The new condition is returned (or NULL in
  * case of low memory). Supports multiple conditions separated by "or".
  */
-struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, int 
pol)
+struct acl_cond *parse_acl_cond(const char *configfile, const char **args, 
struct list *known_acl, int pol)
 {
        __label__ out_return, out_free_suite, out_free_term;
        int arg, neg;
@@ -1467,7 +1499,7 @@ struct acl_cond *parse_acl_cond(const char **args, struct 
list *known_acl, int p
                        args_new[0] = "";
                        memcpy(args_new + 1, args + arg + 1, (arg_end - arg) * 
sizeof(*args_new));
                        args_new[arg_end - arg] = "";
-                       cur_acl = parse_acl(args_new, known_acl);
+                       cur_acl = parse_acl(configfile, args_new, known_acl);
                        free(args_new);
 
                        if (!cur_acl)
@@ -1540,7 +1572,7 @@ struct acl_cond *build_acl_cond(const char *file, int 
line, struct proxy *px, co
        else
                return NULL;
 
-       cond = parse_acl_cond(args, &px->acl, pol);
+       cond = parse_acl_cond(file, args, &px->acl, pol);
        if (!cond)
                return NULL;
 
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 5b942e2..34937b0 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2020,7 +2020,7 @@ int cfg_parse_listen(const char *file, int linenum, char 
**args, int kwm)
                        err_code |= ERR_ALERT | ERR_FATAL;
                }
 
-               if (parse_acl((const char **)args + 1, &curproxy->acl) == NULL) 
{
+               if (parse_acl(file, (const char **)args + 1, &curproxy->acl) == 
NULL) {
                        Alert("parsing [%s:%d] : error detected while parsing 
ACL '%s'.\n",
                              file, linenum, args[1]);
                        err_code |= ERR_ALERT | ERR_FATAL;
-- 
1.7.5.1.217.g4e3aa


Reply via email to