Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12:Update
augeas
bnc925225-aug_escape.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File bnc925225-aug_escape.patch of Package augeas
diff --git a/src/augeas.c b/src/augeas.c index b31560d..ba50071 100644 --- a/src/augeas.c +++ b/src/augeas.c @@ -1984,6 +1984,20 @@ int aug_transform(struct augeas *aug, const char *lens, return result; } +int aug_escape_name(augeas *aug, const char *in, char **out) { + int result; + + api_entry(aug); + ARG_CHECK(in == NULL, aug, "aug_escape_name: IN must not be NULL"); + ARG_CHECK(out == NULL, aug, "aug_escape_name: OUT must not be NULL"); + + result = pathx_escape_name(in, out); + ERR_NOMEM(result < 0, aug); + error: + api_exit(aug); + return result; +} + int aug_print(const struct augeas *aug, FILE *out, const char *pathin) { struct pathx *p; int result; diff --git a/src/augeas.h b/src/augeas.h index b4d6003..3225651 100644 --- a/src/augeas.h +++ b/src/augeas.h @@ -370,6 +370,23 @@ int aug_text_retrieve(struct augeas *aug, const char *lens, const char *node_in, const char *path, const char *node_out); +/* Function: aug_escape_name + * + * Escape special characters in a string such that it can be used as part + * of a path expressions and only matches a node named exactly + * IN. Characters that have special meanings in path expressions, such as + * '[' and ']' are prefixed with a '\\'. Note that this function assumes + * that it is passed a name, not a path, and will therefore escape '/', + * too. + * + * On return, *OUT is NULL if IN does not need any escaping at all, and + * points to an escaped copy of IN otherwise. + * + * Returns: + * 0 on success, or a negative value on failure + */ +int aug_escape_name(augeas *aug, const char *in, char **out); + /* Function: aug_print * * Print each node matching PATH and its descendants to OUT. diff --git a/src/augeas_sym.version b/src/augeas_sym.version index 3740f67..c128e8d 100644 --- a/src/augeas_sym.version +++ b/src/augeas_sym.version @@ -65,3 +65,8 @@ AUGEAS_0.18.0 { global: aug_cp; } AUGEAS_0.16.0; + +AUGEAS_0.19.0 { + global: + aug_escape_name; +} AUGEAS_0.18.0; \ No newline at end of file diff --git a/src/internal.c b/src/internal.c index b9e3eab..e149fe6 100644 --- a/src/internal.c +++ b/src/internal.c @@ -336,6 +336,8 @@ char *path_expand(struct tree *tree, const char *ppath) { char *path; const char *label; + char *escaped = NULL; + int cnt = 0, ind = 0, r; list_for_each(t, siblings) { @@ -356,11 +358,21 @@ char *path_expand(struct tree *tree, const char *ppath) { else label = tree->label; + r = pathx_escape_name(label, &escaped); + if (r < 0) + return NULL; + + if (escaped != NULL) + label = escaped; + if (cnt > 1) { r = asprintf(&path, "%s/%s[%d]", ppath, label, ind); } else { r = asprintf(&path, "%s/%s", ppath, label); } + + free(escaped); + if (r == -1) return NULL; return path; diff --git a/src/internal.h b/src/internal.h index 0b4d5ac..85d8ee3 100644 --- a/src/internal.h +++ b/src/internal.h @@ -575,6 +575,16 @@ void pathx_symtab_remove_descendants(struct pathx_symtab *symtab, const struct tree *tree); void free_symtab(struct pathx_symtab *symtab); +/* Escape a name so that it is safe to pass to parse_name and have it + * interpreted as the literal name of a path component. + * + * On return, *OUT will be NULL if IN does not need escaping, otherwise it + * will contain an escaped copy of IN which the caller must free. + * + * Returns -1 if it failed to allocate memory for *OUT, 0 on success + */ +int pathx_escape_name(const char *in, char **out); + /* Debug helpers, all defined in internal.c. When ENABLE_DEBUG is not * set, they compile to nothing. */ diff --git a/src/pathx.c b/src/pathx.c index 467e5d7..0c4974d 100644 --- a/src/pathx.c +++ b/src/pathx.c @@ -123,6 +123,15 @@ static const char *const axis_names[] = { static const char *const axis_sep = "::"; +/* The characters that can follow a name in a location expression (aka path) + * The parser will assume that name (path component) is finished when it + * encounters any of these characters, unless they are escaped by preceding + * them with a '\\'. + * + * See parse_name for the gory details + */ +static const char const name_follow[] = "][|/=()!,"; + /* Doubly linked list of location steps. Besides the information from the * path expression, also contains information to iterate over a node set, * in particular, the context node CTX for the step, and the current node @@ -1607,17 +1616,43 @@ static void push_new_binary_op(enum binary_op op, struct state *state) { push_expr(expr, state); } +int pathx_escape_name(const char *in, char **out) { + const char *p; + int num_to_escape = 0; + char *s; + + *out = NULL; + + for (p = in; *p; p++) { + if (strchr(name_follow, *p) || isspace(*p)) + num_to_escape += 1; + } + + if (num_to_escape == 0) + return 0; + + if (ALLOC_N(*out, strlen(in) + num_to_escape + 1) < 0) + return -1; + + for (p = in, s = *out; *p; p++) { + if (strchr(name_follow, *p) || isspace(*p)) + *s++ = '\\'; + *s++ = *p; + } + *s = '\0'; + return 0; +} + /* * NameNoWS ::= [^][|/\= \t\n] | \\. * NameWS ::= [^][|/\=] | \\. * Name ::= NameNoWS NameWS* NameNoWS | NameNoWS */ static char *parse_name(struct state *state) { - static const char const follow[] = "][|/=()!,"; const char *s = state->pos; char *result; - while (*state->pos != '\0' && strchr(follow, *state->pos) == NULL) { + while (*state->pos != '\0' && strchr(name_follow, *state->pos) == NULL) { /* This is a hack: since we allow spaces in names, we need to avoid * gobbling up stuff that is in follow(Name), e.g. 'or' so that * things like [name1 or name2] still work. diff --git a/src/transform.c b/src/transform.c index 1026912..7a355d2 100644 --- a/src/transform.c +++ b/src/transform.c @@ -1330,21 +1330,34 @@ int text_retrieve(struct augeas *aug, const char *lens_name, } int remove_file(struct augeas *aug, struct tree *tree) { - char *path = NULL; - const char *filename = NULL; const char *err_status = NULL; char *dyn_err_status = NULL; char *augsave = NULL, *augorig = NULL, *augorig_canon = NULL; + struct tree *path = NULL; + const char *file_path = NULL; + char *meta_path = NULL; int r; - path = path_of_tree(tree); + path = tree_child(tree, s_path); if (path == NULL) { + err_status = "no child called 'path' for file entry"; + goto error; + } + file_path = path->value + strlen(AUGEAS_FILES_TREE); + path = NULL; + + if (file_path == NULL) { + err_status = "no path for file"; + goto error; + } + + meta_path = path_of_tree(tree); + if (meta_path == NULL) { err_status = "path_of_tree"; goto error; } - filename = path + strlen(AUGEAS_META_FILES); - if ((augorig = strappend(aug->root, filename + 1)) == NULL) { + if ((augorig = strappend(aug->root, file_path)) == NULL) { err_status = "root_file"; goto error; } @@ -1359,7 +1372,7 @@ int remove_file(struct augeas *aug, struct tree *tree) { } } - r = file_saved_event(aug, path + strlen(AUGEAS_META_TREE)); + r = file_saved_event(aug, meta_path + strlen(AUGEAS_META_TREE)); if (r < 0) { err_status = "saved_event"; goto error; @@ -1389,9 +1402,10 @@ int remove_file(struct augeas *aug, struct tree *tree) { goto error; } } + path = NULL; tree_unlink(aug, tree); done: - free(path); + free(meta_path); free(augorig); free(augorig_canon); free(augsave); @@ -1400,9 +1414,9 @@ int remove_file(struct augeas *aug, struct tree *tree) { { const char *emsg = dyn_err_status == NULL ? err_status : dyn_err_status; - store_error(aug, filename, path, emsg, errno, NULL, NULL); + store_error(aug, file_path, meta_path, emsg, errno, NULL, NULL); } - free(path); + free(meta_path); free(augorig); free(augorig_canon); free(augsave); diff --git a/tests/run.tests b/tests/run.tests index 39f8153..c74b644 100644 --- a/tests/run.tests +++ b/tests/run.tests @@ -440,13 +440,13 @@ test set-escaped-path-bracket 2 set /white\ space/\[section value print /white\ space/\[section prints - /white space/[section = "value" + /white\ space/\[section = "value" test set-squote-escaped-bracket 2 set '/augeas/\[section' value print '/augeas/\[section' prints - /augeas/[section = "value" + /augeas/\[section = "value" test set-squote-escaped-path 2 set '/white\ space' value diff --git a/tests/test-api.c b/tests/test-api.c index 9367c27..cc2cc87 100644 --- a/tests/test-api.c +++ b/tests/test-api.c @@ -625,6 +625,23 @@ static void testTextRetrieve(CuTest *tc) { CuAssertStrEquals(tc, hosts, hosts_out); } +static void testAugEscape(CuTest *tc) { + static const char *const in = "a/[]b|=c()!, \td"; + static const char *const exp = "a\\/\\[\\]b\\|\\=c\\(\\)\\!\\,\\ \\\td"; + char *out; + struct augeas *aug; + int r; + + aug = aug_init(root, loadpath, AUG_NO_STDINC|AUG_NO_LOAD); + CuAssertPtrNotNull(tc, aug); + + r = aug_escape_name(aug, in, &out); + CuAssertRetSuccess(tc, r); + + CuAssertStrEquals(tc, out, exp); + free(out); +} + int main(void) { char *output = NULL; CuSuite* suite = CuSuiteNew(); @@ -643,6 +660,7 @@ int main(void) { SUITE_ADD_TEST(suite, testToXml); SUITE_ADD_TEST(suite, testTextStore); SUITE_ADD_TEST(suite, testTextRetrieve); + SUITE_ADD_TEST(suite, testAugEscape); abs_top_srcdir = getenv("abs_top_srcdir"); if (abs_top_srcdir == NULL) diff --git a/tests/test-save.c b/tests/test-save.c index f28f626..2fe90a9 100644 --- a/tests/test-save.c +++ b/tests/test-save.c @@ -266,6 +266,58 @@ static void testUmask022(CuTest *tc) { testUmask(tc, 0022, 0644); } +/* Test that handling of 'strange' characters in path names works as + * expected. In particular, that paths with characters that have special + * meaning in path expressions are escaped properly. + * + * This test isn't all that specific to save, but since these tests set up + * a copy of tests/root/ that is modifiable, it was convenient to put this + * test here. + */ +static void testPathEscaping(CuTest *tc) { + /* Path expression with characters escaped */ + static const char *const weird = + "/files/etc/sysconfig/network-scripts/ifcfg-weird\\ \\[\\!\\]\\ \\(used\\ to\\ fail\\)"; + /* Path without any escaping */ + static const char *const weird_no_escape = + "/files/etc/sysconfig/network-scripts/ifcfg-weird [!] (used to fail)"; + + char *fname = NULL, *s = NULL; + const char *v; + int r; + + /* Construct the file name in the file system and check the file is there */ + r = asprintf(&fname, "%s%s", root, weird_no_escape + strlen("/files")); + CuAssertPositive(tc, r); + + r = access(fname, R_OK); + CuAssertIntEquals(tc, 0, r); + + /* Make sure weird is in the tree */ + r = aug_match(aug, weird, NULL); + CuAssertIntEquals(tc, 1, r); + + /* Make sure we can get to the metadata about weird */ + r = asprintf(&s, "/augeas%s/path", weird); + CuAssertPositive(tc, r); + + r = aug_get(aug, s, &v); + CuAssertIntEquals(tc, 1, r); + CuAssertStrEquals(tc, weird_no_escape, v); + + /* Delete it from the tree and save it; make sure it gets removed + from the file system */ + r = aug_rm(aug, weird); + CuAssertPositive(tc, r); + + r = aug_save(aug); + CuAssertRetSuccess(tc, r); + + r = access(fname, R_OK); + CuAssertIntEquals(tc, -1, r); + CuAssertIntEquals(tc, ENOENT, errno); +} + int main(void) { char *output = NULL; CuSuite* suite = CuSuiteNew(); @@ -293,6 +345,7 @@ int main(void) { SUITE_ADD_TEST(suite, testUmask077); SUITE_ADD_TEST(suite, testUmask027); SUITE_ADD_TEST(suite, testUmask022); + SUITE_ADD_TEST(suite, testPathEscaping); CuSuiteRun(suite); CuSuiteSummary(suite, &output); diff --git a/tests/xpath.tests b/tests/xpath.tests index 41bb555..e89cf98 100644 --- a/tests/xpath.tests +++ b/tests/xpath.tests @@ -278,7 +278,7 @@ test union (/files/etc/yum.conf | /files/etc/yum.repos.d/*)/*/gpgcheck # Paths with whitespace in them test php1 $php/mail function - /files/etc/php.ini/mail function + /files/etc/php.ini/mail\ function test php2 $php[mail function] /files/etc/php.ini @@ -287,10 +287,10 @@ test php3 $php[count(mail function) = 1] /files/etc/php.ini test php4 $php/mail function/SMTP - /files/etc/php.ini/mail function/SMTP = localhost + /files/etc/php.ini/mail\ function/SMTP = localhost test php5 $php/mail\ function - /files/etc/php.ini/mail function + /files/etc/php.ini/mail\ function test expr-or /files/etc/group/root/*[self::gid or self::user] /files/etc/group/root/gid = 0 @@ -315,3 +315,19 @@ test ctx_file etc/network/interfaces test ctx_file_pred etc/network/interfaces/iface[. = "lo"] /files/etc/network/interfaces/iface[1] = lo + +# Test matching with characters that need escaping in the filename +test escape1 /files/etc/sysconfig/network-scripts/* + /files/etc/sysconfig/network-scripts/ifcfg-eth0 + /files/etc/sysconfig/network-scripts/ifcfg-wlan0 + /files/etc/sysconfig/network-scripts/ifcfg-weird\ \[\!\]\ \(used\ to\ fail\) + /files/etc/sysconfig/network-scripts/ifcfg-Auto_FRITZ\!Box_Fon_WLAN_7112 + /files/etc/sysconfig/network-scripts/ifcfg-lo + /files/etc/sysconfig/network-scripts/ifcfg-Auto-ALICE-WLAN38_\(automatisch\) + /files/etc/sysconfig/network-scripts/ifcfg-br0 + +test escape2 /files/etc/sysconfig/network-scripts/ifcfg-weird\ \[\!\]\ \(used\ to\ fail\)/DEVICE + /files/etc/sysconfig/network-scripts/ifcfg-weird\ \[\!\]\ \(used\ to\ fail\)/DEVICE = weird + +test escape3 /files/etc/sysconfig/network-scripts/*[DEVICE = 'weird'] + /files/etc/sysconfig/network-scripts/ifcfg-weird\ \[\!\]\ \(used\ to\ fail\)
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor