From c7935c3ce8f2239d17c739ed718b8b9328cd2035 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Jun 30 2010 01:31:11 +0000 Subject: Address BZ 600141 with upstream patches --- diff --git a/augeas-0.7.2-01-0c43ca0f.patch b/augeas-0.7.2-01-0c43ca0f.patch new file mode 100644 index 0000000..b5bb157 --- /dev/null +++ b/augeas-0.7.2-01-0c43ca0f.patch @@ -0,0 +1,33 @@ +From 0c43ca0f0ce9008a991c2a2d99e938f4a60712a3 Mon Sep 17 00:00:00 2001 +From: David Lutterkort +Date: Tue, 29 Jun 2010 14:12:47 -0700 +Subject: [PATCH 1/9] * src/augeas.c (aug_defvar): use constants to create /augeas/variables + +--- + src/augeas.c | 3 ++- + 1 files changed, 2 insertions(+), 1 deletions(-) + +diff --git a/src/augeas.c b/src/augeas.c +index c21afe9..7b51a77 100644 +--- a/src/augeas.c ++++ b/src/augeas.c +@@ -41,6 +41,7 @@ static const char *const s_load = "load"; + static const char *const s_pathx = "pathx"; + static const char *const s_error = "error"; + static const char *const s_pos = "pos"; ++static const char *const s_vars = "variables"; + + #define TREE_HIDDEN(tree) ((tree)->label == NULL) + +@@ -529,7 +530,7 @@ int aug_defvar(augeas *aug, const char *name, const char *expr) { + ERR_BAIL(aug); + + /* Record the definition of the variable */ +- struct tree *tree = tree_path_cr(aug->origin, 2, "augeas", "variables"); ++ struct tree *tree = tree_path_cr(aug->origin, 2, s_augeas, s_vars); + ERR_NOMEM(tree == NULL, aug); + if (expr == NULL) { + tree = tree_child(tree, name); +-- +1.6.6.1 + diff --git a/augeas-0.7.2-02-6c713a08.patch b/augeas-0.7.2-02-6c713a08.patch new file mode 100644 index 0000000..3fda10c --- /dev/null +++ b/augeas-0.7.2-02-6c713a08.patch @@ -0,0 +1,28 @@ +From 6c713a0855f6f2f144ebc2240fc4ec0e3470aacc Mon Sep 17 00:00:00 2001 +From: David Lutterkort +Date: Tue, 29 Jun 2010 14:13:13 -0700 +Subject: [PATCH 2/9] * src/augeas.c (aug_init): create /augeas/variables on startup + +--- + src/augeas.c | 5 +++-- + 1 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/augeas.c b/src/augeas.c +index 7b51a77..9ca26b6 100644 +--- a/src/augeas.c ++++ b/src/augeas.c +@@ -406,8 +406,9 @@ struct augeas *aug_init(const char *root, const char *loadpath, + } else { + aug_set(result, AUGEAS_META_SAVE_MODE, AUG_SAVE_OVERWRITE_TEXT); + } +- /* Make sure we always have /files */ +- aug_set(result, AUGEAS_FILES_TREE, NULL); ++ /* Make sure we always have /files and /augeas/variables */ ++ tree_path_cr(result->origin, 1, s_files); ++ tree_path_cr(result->origin, 2, s_augeas, s_vars); + + if (interpreter_init(result) == -1) + goto error; +-- +1.6.6.1 + diff --git a/augeas-0.7.2-03-984ff1b8.patch b/augeas-0.7.2-03-984ff1b8.patch new file mode 100644 index 0000000..7cec6e1 --- /dev/null +++ b/augeas-0.7.2-03-984ff1b8.patch @@ -0,0 +1,56 @@ +From 984ff1b8f37c64339ed9663d2b052dadbea82032 Mon Sep 17 00:00:00 2001 +From: David Lutterkort +Date: Tue, 29 Jun 2010 14:21:42 -0700 +Subject: [PATCH 3/9] Redefine all variables upon load + +This is a slight change in behavior: before, we used to just discard the +contents of all variables upon load. Now, we redefine variables by +evaluating the expression with which they were defined initially again. + +The change in behavior is backwards compatible, since at worst, users will +redefine variables themselves after an aug_load +--- + src/augeas.c | 6 ++++++ + tests/test-load.c | 2 +- + 2 files changed, 7 insertions(+), 1 deletions(-) + +diff --git a/src/augeas.c b/src/augeas.c +index 9ca26b6..2e5f296 100644 +--- a/src/augeas.c ++++ b/src/augeas.c +@@ -447,6 +447,7 @@ int aug_load(struct augeas *aug) { + struct tree *meta_files = tree_child_cr(meta, s_files); + struct tree *files = tree_child_cr(aug->origin, s_files); + struct tree *load = tree_child_cr(meta, s_load); ++ struct tree *vars = tree_child_cr(meta, s_vars); + + api_entry(aug); + +@@ -461,6 +462,11 @@ int aug_load(struct augeas *aug) { + } + tree_clean(aug->origin); + ++ list_for_each(v, vars->children) { ++ aug_defvar(aug, v->label, v->value); ++ ERR_BAIL(aug); ++ } ++ + api_exit(aug); + return 0; + error: +diff --git a/tests/test-load.c b/tests/test-load.c +index 547e222..e4dd38a 100644 +--- a/tests/test-load.c ++++ b/tests/test-load.c +@@ -205,7 +205,7 @@ static void testLoadDefined(CuTest *tc) { + CuAssertRetSuccess(tc, r); + + r = aug_match(aug, "$v", NULL); +- CuAssertIntEquals(tc, 0, r); ++ CuAssertIntEquals(tc, 2, r); + + aug_close(aug); + } +-- +1.6.6.1 + diff --git a/augeas-0.7.2-04-218003a8.patch b/augeas-0.7.2-04-218003a8.patch new file mode 100644 index 0000000..286ecfb --- /dev/null +++ b/augeas-0.7.2-04-218003a8.patch @@ -0,0 +1,96 @@ +From 218003a813acae99370d45157fe57589b8a8685c Mon Sep 17 00:00:00 2001 +From: David Lutterkort +Date: Tue, 29 Jun 2010 14:50:14 -0700 +Subject: [PATCH 4/9] Move 'run' test utility to cutest.[ch] + + * tests/cutest.h (run): add prototype + * tests/cutest.c (run): add impl + * tests/test-save.c (run): remove +--- + tests/cutest.c | 24 ++++++++++++++++++++++++ + tests/cutest.h | 2 ++ + tests/test-save.c | 20 -------------------- + 3 files changed, 26 insertions(+), 20 deletions(-) + +diff --git a/tests/cutest.c b/tests/cutest.c +index 4cfe0fc..120e31c 100644 +--- a/tests/cutest.c ++++ b/tests/cutest.c +@@ -296,6 +296,30 @@ void CuSuiteDetails(CuSuite* testSuite, char **details) { + } + + /* ++ * Test utilities ++ */ ++void run(CuTest *tc, const char *format, ...) { ++ char *command; ++ va_list args; ++ int r; ++ ++ va_start(args, format); ++ r = vasprintf(&command, format, args); ++ va_end (args); ++ if (r < 0) ++ CuFail(tc, "Failed to format command (out of memory)"); ++ r = system(command); ++ if (r < 0 || (WIFEXITED(r) && WEXITSTATUS(r) != 0)) { ++ char *msg; ++ r = asprintf(&msg, "Command %s failed with status %d\n", ++ command, WEXITSTATUS(r)); ++ CuFail(tc, msg); ++ free(msg); ++ } ++ free(command); ++} ++ ++/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 +diff --git a/tests/cutest.h b/tests/cutest.h +index ae9fe0f..a667e50 100644 +--- a/tests/cutest.h ++++ b/tests/cutest.h +@@ -120,6 +120,8 @@ void CuSuiteRun(CuSuite* testSuite); + void CuSuiteSummary(CuSuite* testSuite, char **summary); + void CuSuiteDetails(CuSuite* testSuite, char **details); + ++/* Run a command */ ++void run(CuTest *tc, const char *format, ...); + #endif /* CU_TEST_H */ + + /* +diff --git a/tests/test-save.c b/tests/test-save.c +index c563ad3..daeace3 100644 +--- a/tests/test-save.c ++++ b/tests/test-save.c +@@ -40,26 +40,6 @@ struct augeas *aug = NULL; + exit(EXIT_FAILURE); \ + } while(0) + +-static void run(CuTest *tc, const char *format, ...) { +- char *command; +- va_list args; +- int r; +- +- va_start(args, format); +- r = vasprintf(&command, format, args); +- va_end (args); +- if (r < 0) +- CuFail(tc, "Failed to format command (out of memory)"); +- r = system(command); +- if (r < 0 || (WIFEXITED(r) && WEXITSTATUS(r) != 0)) { +- char *msg; +- r = asprintf(&msg, "Command %s failed with status %d\n", +- command, WEXITSTATUS(r)); +- CuFail(tc, msg); +- free(msg); +- } +-} +- + static void setup(CuTest *tc) { + char *lensdir; + +-- +1.6.6.1 + diff --git a/augeas-0.7.2-05-21d39d66.patch b/augeas-0.7.2-05-21d39d66.patch new file mode 100644 index 0000000..60af029 --- /dev/null +++ b/augeas-0.7.2-05-21d39d66.patch @@ -0,0 +1,98 @@ +From 21d39d66b9c2d827cbede4ace1fe0fc0b637992b Mon Sep 17 00:00:00 2001 +From: David Lutterkort +Date: Tue, 29 Jun 2010 15:01:03 -0700 +Subject: [PATCH 5/9] * src/test-load.c (testLoadSave): work off a writable /etc/hosts + +This addresses a FIXME that could lead to use not detecting incorrect behavior +--- + tests/test-load.c | 53 +++++++++++++++++++++++++++++++++++++---------------- + 1 files changed, 37 insertions(+), 16 deletions(-) + +diff --git a/tests/test-load.c b/tests/test-load.c +index e4dd38a..a8107a1 100644 +--- a/tests/test-load.c ++++ b/tests/test-load.c +@@ -32,7 +32,8 @@ + #define CuAssertRetSuccess(tc, n) CuAssertTrue(tc, (n) == 0) + + static const char *abs_top_srcdir; +-static char *root; ++static const char *abs_top_builddir; ++static char *root = NULL; + static char *loadpath; + + #define die(msg) \ +@@ -41,6 +42,36 @@ static char *loadpath; + exit(EXIT_FAILURE); \ + } while(0) + ++static struct augeas *setup_writable_hosts(CuTest *tc) { ++ char *etcdir, *build_root; ++ struct augeas *aug = NULL; ++ int r; ++ ++ if (asprintf(&build_root, "%s/build/test-load/%s", ++ abs_top_builddir, tc->name) < 0) { ++ CuFail(tc, "failed to set build_root"); ++ } ++ ++ if (asprintf(&etcdir, "%s/etc", build_root) < 0) ++ CuFail(tc, "asprintf etcdir failed"); ++ ++ run(tc, "test -d %s && chmod -R u+w %s || :", build_root, build_root); ++ run(tc, "rm -rf %s", build_root); ++ run(tc, "mkdir -p %s", etcdir); ++ run(tc, "cp -pr %s/etc/hosts %s", root, etcdir); ++ run(tc, "chmod -R u+w %s", build_root); ++ ++ aug = aug_init(build_root, loadpath, AUG_NO_MODL_AUTOLOAD); ++ CuAssertPtrNotNull(tc, aug); ++ ++ r = aug_set(aug, "/augeas/load/Hosts/lens", "Hosts.lns"); ++ CuAssertRetSuccess(tc, r); ++ ++ r = aug_set(aug, "/augeas/load/Hosts/incl", "/etc/hosts"); ++ CuAssertRetSuccess(tc, r); ++ ++ return aug; ++} + + static void testDefault(CuTest *tc) { + augeas *aug = NULL; +@@ -162,21 +193,7 @@ static void testLoadSave(CuTest *tc) { + augeas *aug = NULL; + int r; + +- /* FIXME: This test behaves properly during distcheck, since srcdir +- * is writeprotected, making an incorrect attempt to write +- * /etc/hosts.augnew fail; during normal 'make check' the test will +- * succeed. +- * To address this, we should copy the files fro, tests/root into +- * another directory and 'chmod a-w /etc' in that root +- */ +- aug = aug_init(root, loadpath, AUG_NO_MODL_AUTOLOAD|AUG_SAVE_NOOP); +- CuAssertPtrNotNull(tc, aug); +- +- r = aug_set(aug, "/augeas/load/Hosts/lens", "Hosts.lns"); +- CuAssertRetSuccess(tc, r); +- +- r = aug_set(aug, "/augeas/load/Hosts/incl", "/etc/hosts"); +- CuAssertRetSuccess(tc, r); ++ aug = setup_writable_hosts(tc); + + r = aug_load(aug); + CuAssertRetSuccess(tc, r); +@@ -262,6 +279,10 @@ int main(void) { + if (abs_top_srcdir == NULL) + die("env var abs_top_srcdir must be set"); + ++ abs_top_builddir = getenv("abs_top_builddir"); ++ if (abs_top_builddir == NULL) ++ die("env var abs_top_builddir must be set"); ++ + if (asprintf(&root, "%s/tests/root", abs_top_srcdir) < 0) { + die("failed to set root"); + } +-- +1.6.6.1 + diff --git a/augeas-0.7.2-06-11b85805.patch b/augeas-0.7.2-06-11b85805.patch new file mode 100644 index 0000000..cf8b002 --- /dev/null +++ b/augeas-0.7.2-06-11b85805.patch @@ -0,0 +1,55 @@ +From 11b85805955557ae87494fff57ead04be6ab90b6 Mon Sep 17 00:00:00 2001 +From: David Lutterkort +Date: Tue, 29 Jun 2010 15:43:07 -0700 +Subject: [PATCH 6/9] Add xstrtoint64 to internal.[ch] + +The implementation is directly from libvirt + + * src/internal.h (xstrtoint64): add prototype + * src/internal.c (xstrtoint64): add impl +--- + src/internal.c | 13 +++++++++++++ + src/internal.h | 3 +++ + 2 files changed, 16 insertions(+), 0 deletions(-) + +diff --git a/src/internal.c b/src/internal.c +index befd3af..8419ca2 100644 +--- a/src/internal.c ++++ b/src/internal.c +@@ -392,6 +392,19 @@ int xasprintf(char **strp, const char *format, ...) { + return result; + } + ++/* From libvirt's src/xen/block_stats.c */ ++int xstrtoint64(char const *s, int base, int64_t *result) { ++ long long int lli; ++ char *p; ++ ++ errno = 0; ++ lli = strtoll(s, &p, base); ++ if (errno || !(*p == 0 || *p == '\n') || p == s || (int64_t) lli != lli) ++ return -1; ++ *result = lli; ++ return 0; ++} ++ + void calc_line_ofs(const char *text, size_t pos, size_t *line, size_t *ofs) { + *line = 1; + *ofs = 0; +diff --git a/src/internal.h b/src/internal.h +index b2a402f..51aa025 100644 +--- a/src/internal.h ++++ b/src/internal.h +@@ -272,6 +272,9 @@ const char *xstrerror(int errnum, char *buf, size_t len); + /* Like asprintf, but set *STRP to NULL on error */ + int xasprintf(char **strp, const char *format, ...); + ++/* Convert S to RESULT with error checking */ ++int xstrtoint64(char const *s, int base, int64_t *result); ++ + /* Calculate line and column number of character POS in TEXT */ + void calc_line_ofs(const char *text, size_t pos, size_t *line, size_t *ofs); + +-- +1.6.6.1 + diff --git a/augeas-0.7.2-07-72c636de.patch b/augeas-0.7.2-07-72c636de.patch new file mode 100644 index 0000000..548f3c7 --- /dev/null +++ b/augeas-0.7.2-07-72c636de.patch @@ -0,0 +1,42 @@ +From 72c636decaa8b8c1764710c60f9932892c411207 Mon Sep 17 00:00:00 2001 +From: David Lutterkort +Date: Tue, 29 Jun 2010 15:44:35 -0700 +Subject: [PATCH 7/9] Make tree_clean available outside of augeas.c + + * src/augeas.c (tree_clean): remove 'static' + * src/internal.h (tree_clean): add prototype +--- + src/augeas.c | 3 +-- + src/internal.h | 2 ++ + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/augeas.c b/src/augeas.c +index 2e5f296..5ca1789 100644 +--- a/src/augeas.c ++++ b/src/augeas.c +@@ -74,8 +74,7 @@ static void tree_mark_dirty(struct tree *tree) { + tree->dirty = 1; + } + +-/* Clear the dirty flag in the whole TREE */ +-static void tree_clean(struct tree *tree) { ++void tree_clean(struct tree *tree) { + if (tree->dirty) { + list_for_each(c, tree->children) + tree_clean(c); +diff --git a/src/internal.h b/src/internal.h +index 51aa025..f1e6f3a 100644 +--- a/src/internal.h ++++ b/src/internal.h +@@ -379,6 +379,8 @@ int dump_tree(FILE *out, struct tree *tree); + int tree_equal(const struct tree *t1, const struct tree *t2); + char *path_expand(struct tree *tree, const char *ppath); + char *path_of_tree(struct tree *tree); ++/* Clear the dirty flag in the whole TREE */ ++void tree_clean(struct tree *tree); + /* Return first child with label LABEL or NULL */ + struct tree *tree_child(struct tree *tree, const char *label); + /* Return first existing child with label LABEL or create one. Return NULL +-- +1.6.6.1 + diff --git a/augeas-0.7.2-08-0695bdd0.patch b/augeas-0.7.2-08-0695bdd0.patch new file mode 100644 index 0000000..3951a14 --- /dev/null +++ b/augeas-0.7.2-08-0695bdd0.patch @@ -0,0 +1,68 @@ +From 0695bdd057b2cbfc6146ac498179c0f162ee889f Mon Sep 17 00:00:00 2001 +From: David Lutterkort +Date: Tue, 29 Jun 2010 15:46:53 -0700 +Subject: [PATCH 8/9] Add utility tree_store_value to avoid unnecessary strdup's + + * src/internal.h (tree_store_value): add prototype + * src/augeas.c (tree_store_value): new function; (tree_set_value): use + tree_store_value +--- + src/augeas.c | 18 ++++++++++++++---- + src/internal.h | 5 ++++- + 2 files changed, 18 insertions(+), 5 deletions(-) + +diff --git a/src/augeas.c b/src/augeas.c +index 5ca1789..744b699 100644 +--- a/src/augeas.c ++++ b/src/augeas.c +@@ -156,17 +156,27 @@ struct tree *tree_find_cr(struct augeas *aug, const char *path) { + return result; + } + +-int tree_set_value(struct tree *tree, const char *value) { ++void tree_store_value(struct tree *tree, char **value) { + if (tree->value != NULL) { + free(tree->value); + tree->value = NULL; + } ++ if (*value != NULL) { ++ tree->value = *value; ++ *value = NULL; ++ } ++ tree_mark_dirty(tree); ++} ++ ++int tree_set_value(struct tree *tree, const char *value) { ++ char *v = NULL; ++ + if (value != NULL) { +- tree->value = strdup(value); +- if (tree->value == NULL) ++ v = strdup(value); ++ if (v == NULL) + return -1; + } +- tree_mark_dirty(tree); ++ tree_store_value(tree, &v); + return 0; + } + +diff --git a/src/internal.h b/src/internal.h +index f1e6f3a..7513d47 100644 +--- a/src/internal.h ++++ b/src/internal.h +@@ -389,7 +389,10 @@ struct tree *tree_child_cr(struct tree *tree, const char *label); + /* Create a path in the tree; nodes along the path are looked up with + * tree_child_cr */ + struct tree *tree_path_cr(struct tree *tree, int n, ...); +-/* Set the value of TREE and update dirty flags */ ++/* Store VALUE directly as the value of TREE and set VALUE to NULL. ++ * Update dirty flags */ ++void tree_store_value(struct tree *tree, char **value); ++/* Set the value of TREE to a copy of VALUE and update dirty flags */ + int tree_set_value(struct tree *tree, const char *value); + /* Cleanly remove all children of TREE, but leave TREE itself unchanged */ + void tree_unlink_children(struct augeas *aug, struct tree *tree); +-- +1.6.6.1 + diff --git a/augeas-0.7.2-09-5ee81630.patch b/augeas-0.7.2-09-5ee81630.patch new file mode 100644 index 0000000..09ea6d2 --- /dev/null +++ b/augeas-0.7.2-09-5ee81630.patch @@ -0,0 +1,501 @@ +From 5ee8163051be8214507c13c86171ac90ca7cb91f Mon Sep 17 00:00:00 2001 +From: David Lutterkort +Date: Tue, 29 Jun 2010 15:32:44 -0700 +Subject: [PATCH 9/9] Avoid unnecessary file parsing when reloading the tree + +We used to reparse every file we knew about upon aug_load. Now, we only +reparse files if the file has changed on disk. + +We test a few scenarios to make sure aug_load retains its behavior of +obliterating the tree and filling it with the latest from disk. This +includes throwing away unsaved changes or trees that have been deleted. +--- + src/augeas.c | 72 ++++++++++++++++++++++++++- + src/transform.c | 78 +++++++++++++++++++++++++++-- + tests/cutest.c | 17 ++++++ + tests/cutest.h | 4 ++ + tests/test-load.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + tests/xpath.tests | 1 + + 6 files changed, 307 insertions(+), 7 deletions(-) + +diff --git a/src/augeas.c b/src/augeas.c +index 744b699..45c2207 100644 +--- a/src/augeas.c ++++ b/src/augeas.c +@@ -451,6 +451,51 @@ void tree_unlink_children(struct augeas *aug, struct tree *tree) { + tree_unlink(tree->children); + } + ++static void tree_mark_files(struct tree *tree) { ++ if (tree_child(tree, "path") != NULL) { ++ tree_mark_dirty(tree); ++ } else { ++ list_for_each(c, tree->children) { ++ tree_mark_files(c); ++ } ++ } ++} ++ ++static void tree_rm_dirty_files(struct augeas *aug, struct tree *tree) { ++ struct tree *p; ++ ++ if (!tree->dirty) ++ return; ++ ++ if ((p = tree_child(tree, "path")) != NULL) { ++ aug_rm(aug, p->value); ++ tree_unlink(tree); ++ } else { ++ struct tree *c = tree->children; ++ while (c != NULL) { ++ struct tree *next = c->next; ++ tree_rm_dirty_files(aug, c); ++ c = next; ++ } ++ } ++} ++ ++static void tree_rm_dirty_leaves(struct augeas *aug, struct tree *tree, ++ struct tree *protect) { ++ if (! tree->dirty) ++ return; ++ ++ struct tree *c = tree->children; ++ while (c != NULL) { ++ struct tree *next = c->next; ++ tree_rm_dirty_leaves(aug, c, protect); ++ c = next; ++ } ++ ++ if (tree != protect && tree->children == NULL) ++ tree_unlink(tree); ++} ++ + int aug_load(struct augeas *aug) { + struct tree *meta = tree_child_cr(aug->origin, s_augeas); + struct tree *meta_files = tree_child_cr(meta, s_files); +@@ -462,13 +507,36 @@ int aug_load(struct augeas *aug) { + + ERR_NOMEM(load == NULL, aug); + +- tree_unlink_children(aug, meta_files); +- tree_unlink_children(aug, files); ++ /* To avoid unnecessary loads of files, we reload an existing file in ++ * several steps: ++ * (1) mark all file nodes under /augeas/files as dirty (and only those) ++ * (2) process all files matched by a lens; we check (in ++ * transform_load) if the file has been modified. If it has, we ++ * reparse it. Either way, we clear the dirty flag. We also need to ++ * reread the file if part or all of it has been modified in the ++ * tree but not been saved yet ++ * (3) remove all files from the tree that still have a dirty entry ++ * under /augeas/files. Those files are not processed by any lens ++ * anymore ++ * (4) Remove entries from /augeas/files and /files that correspond ++ * to directories without any files of interest ++ */ ++ tree_clean(meta_files); ++ tree_mark_files(meta_files); + + list_for_each(xfm, load->children) { + if (transform_validate(aug, xfm) == 0) + transform_load(aug, xfm); + } ++ ++ /* This makes it possible to spot 'directories' that are now empty ++ * because we removed their file contents */ ++ tree_clean(files); ++ ++ tree_rm_dirty_files(aug, meta_files); ++ tree_rm_dirty_leaves(aug, meta_files, meta_files); ++ tree_rm_dirty_leaves(aug, files, files); ++ + tree_clean(aug->origin); + + list_for_each(v, vars->children) { +diff --git a/src/transform.c b/src/transform.c +index 0c56034..00552da 100644 +--- a/src/transform.c ++++ b/src/transform.c +@@ -49,6 +49,7 @@ static const int glob_flags = GLOB_NOSORT; + /* Loaded files are tracked underneath METATREE. When a file with name + * FNAME is loaded, certain entries are made under METATREE / FNAME: + * path : path where tree for FNAME is put ++ * mtime : time of last modification of the file as reported by stat(2) + * lens/info : information about where the applied lens was loaded from + * lens/id : unique hexadecimal id of the lens + * error : indication of errors during processing FNAME, or NULL +@@ -60,6 +61,7 @@ static const int glob_flags = GLOB_NOSORT; + static const char *const s_path = "path"; + static const char *const s_lens = "lens"; + static const char *const s_info = "info"; ++static const char *const s_mtime = "mtime"; + + static const char *const s_error = "error"; + /* These are all put underneath "error" */ +@@ -111,6 +113,59 @@ static bool is_regular_file(const char *path) { + return S_ISREG(st.st_mode); + } + ++static char *mtime_as_string(struct augeas *aug, const char *fname) { ++ int r; ++ struct stat st; ++ char *result = NULL; ++ ++ r = stat(fname, &st); ++ if (r < 0) { ++ /* If we fail to stat, silently ignore the error ++ * and report an impossible mtime */ ++ result = strdup("0"); ++ ERR_NOMEM(result == NULL, aug); ++ } else { ++ r = xasprintf(&result, "%ld", (long) st.st_mtime); ++ ERR_NOMEM(r < 0, aug); ++ } ++ return result; ++ error: ++ FREE(result); ++ return NULL; ++} ++ ++static bool file_current(struct augeas *aug, const char *fname, ++ struct tree *finfo) { ++ struct tree *mtime = tree_child(finfo, s_mtime); ++ struct tree *file = NULL, *path = NULL; ++ int r; ++ struct stat st; ++ int64_t mtime_i; ++ ++ if (mtime == NULL || mtime->value == NULL) ++ return false; ++ ++ r = xstrtoint64(mtime->value, 10, &mtime_i); ++ if (r < 0) { ++ /* Ignore silently and err on the side of caution */ ++ return false; ++ } ++ ++ r = stat(fname, &st); ++ if (r < 0) ++ return false; ++ ++ if (mtime_i != (int64_t) st.st_mtime) ++ return false; ++ ++ path = tree_child(finfo, s_path); ++ if (path == NULL) ++ return false; ++ ++ file = tree_find(aug, path->value); ++ return (file != NULL && ! file->dirty); ++} ++ + static int filter_generate(struct tree *xfm, const char *root, + int *nmatches, char ***matches) { + glob_t globbuf; +@@ -326,7 +381,8 @@ static int store_error(struct augeas *aug, + * Returns 0 on success, -1 on error + */ + static int add_file_info(struct augeas *aug, const char *node, +- struct lens *lens, const char *lens_name) { ++ struct lens *lens, const char *lens_name, ++ const char *filename) { + struct tree *file, *tree; + char *tmp = NULL; + int r; +@@ -348,6 +404,13 @@ static int add_file_info(struct augeas *aug, const char *node, + r = tree_set_value(tree, node); + ERR_NOMEM(r < 0, aug); + ++ /* Set 'mtime' */ ++ tmp = mtime_as_string(aug, filename); ++ ERR_BAIL(aug); ++ tree = tree_child_cr(file, s_mtime); ++ ERR_NOMEM(tree == NULL, aug); ++ tree_store_value(tree, &tmp); ++ + /* Set 'lens/info' */ + tmp = format_info(lens->info); + ERR_NOMEM(tmp == NULL, aug); +@@ -362,6 +425,8 @@ static int add_file_info(struct augeas *aug, const char *node, + r = tree_set_value(tree, lens_name); + ERR_NOMEM(r < 0, aug); + ++ tree_clean(file); ++ + result = 0; + error: + free(path); +@@ -404,7 +469,7 @@ static int load_file(struct augeas *aug, struct lens *lens, + path = file_name_path(aug, filename); + ERR_NOMEM(path == NULL, aug); + +- r = add_file_info(aug, path, lens, lens_name); ++ r = add_file_info(aug, path, lens, lens_name, filename); + if (r < 0) + goto done; + +@@ -602,7 +667,8 @@ int transform_load(struct augeas *aug, struct tree *xfm) { + for (int i=0; i < nmatches; i++) { + const char *filename = matches[i] + strlen(aug->root) - 1; + struct tree *finfo = file_info(aug, filename); +- if (finfo != NULL && tree_child(finfo, s_lens) != NULL) { ++ if (finfo != NULL && !finfo->dirty && ++ tree_child(finfo, s_lens) != NULL) { + const char *s = xfm_lens_name(finfo); + char *fpath = file_name_path(aug, matches[i]); + transform_file_error(aug, "mxfm_load", filename, +@@ -610,9 +676,11 @@ int transform_load(struct augeas *aug, struct tree *xfm) { + s, lens_name); + aug_rm(aug, fpath); + free(fpath); +- } else { ++ } else if (!file_current(aug, matches[i], finfo)) { + load_file(aug, lens, lens_name, matches[i]); + } ++ if (finfo != NULL) ++ finfo->dirty = 0; + FREE(matches[i]); + } + lens_release(lens); +@@ -945,7 +1013,7 @@ int transform_save(struct augeas *aug, struct tree *xfm, + result = 1; + + done: +- r = add_file_info(aug, path, lens, lens_name); ++ r = add_file_info(aug, path, lens, lens_name, filename); + if (r < 0) { + err_status = "file_info"; + result = -1; +diff --git a/tests/cutest.c b/tests/cutest.c +index 120e31c..08dbfd7 100644 +--- a/tests/cutest.c ++++ b/tests/cutest.c +@@ -138,6 +138,23 @@ void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, + CuFailInternal(tc, file, line, string); + } + ++void CuAssertStrNotEqual_LineMsg(CuTest* tc, const char* file, int line, ++ const char* message, ++ const char* expected, const char* actual) { ++ char *string = NULL; ++ ++ if (expected != NULL && actual != NULL && strcmp(expected, actual) != 0) ++ return; ++ ++ if (message != NULL) { ++ asprintf_or_die(&string, "%s: expected <%s> but was <%s>", message, ++ expected, actual); ++ } else { ++ asprintf_or_die(&string, "expected <%s> but was <%s>", expected, actual); ++ } ++ CuFailInternal(tc, file, line, string); ++} ++ + void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, + const char* message, + int expected, int actual) { +diff --git a/tests/cutest.h b/tests/cutest.h +index a667e50..616fb1f 100644 +--- a/tests/cutest.h ++++ b/tests/cutest.h +@@ -61,6 +61,9 @@ void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, + void CuAssertStrEquals_LineMsg(CuTest* tc, + const char* file, int line, const char* message, + const char* expected, const char* actual); ++void CuAssertStrNotEqual_LineMsg(CuTest* tc, ++ const char* file, int line, const char* message, ++ const char* expected, const char* actual); + void CuAssertIntEquals_LineMsg(CuTest* tc, + const char* file, int line, const char* message, + int expected, int actual); +@@ -82,6 +85,7 @@ void CuAssertPtrNotEqual_LineMsg(CuTest* tc, + + #define CuAssertStrEquals(tc,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) + #define CuAssertStrEquals_Msg(tc,ms,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) ++#define CuAssertStrNotEqual(tc,ex,ac) CuAssertStrNotEqual_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) + #define CuAssertIntEquals(tc,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) + #define CuAssertIntEquals_Msg(tc,ms,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) + #define CuAssertDblEquals(tc,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac),(dl)) +diff --git a/tests/test-load.c b/tests/test-load.c +index a8107a1..fe92305 100644 +--- a/tests/test-load.c ++++ b/tests/test-load.c +@@ -70,6 +70,8 @@ static struct augeas *setup_writable_hosts(CuTest *tc) { + r = aug_set(aug, "/augeas/load/Hosts/incl", "/etc/hosts"); + CuAssertRetSuccess(tc, r); + ++ free(build_root); ++ free(etcdir); + return aug; + } + +@@ -262,6 +264,142 @@ static void testDefvarExpr(CuTest *tc) { + aug_close(aug); + } + ++static void testReloadChanged(CuTest *tc) { ++ FILE *fp; ++ augeas *aug = NULL; ++ const char *build_root, *mtime2, *s; ++ char *mtime1; ++ char *hosts = NULL; ++ int r; ++ ++ aug = setup_writable_hosts(tc); ++ ++ r = aug_load(aug); ++ CuAssertRetSuccess(tc, r); ++ ++ r = aug_get(aug, "/augeas/root", &build_root); ++ CuAssertIntEquals(tc, 1, r); ++ ++ r = aug_get(aug, "/augeas/files/etc/hosts/mtime", &s); ++ CuAssertIntEquals(tc, 1, r); ++ mtime1 = strdup(s); ++ CuAssertPtrNotNull(tc, mtime1); ++ ++ /* Tickle /etc/hosts behind augeas' back */ ++ r = asprintf(&hosts, "%setc/hosts", build_root); ++ CuAssertPositive(tc, r); ++ ++ fp = fopen(hosts, "a"); ++ CuAssertPtrNotNull(tc, fp); ++ ++ r = fprintf(fp, "192.168.0.1 other.example.com\n"); ++ CuAssertTrue(tc, r > 0); ++ ++ r = fclose(fp); ++ CuAssertRetSuccess(tc, r); ++ ++ /* Unsaved changes are discarded */ ++ r = aug_set(aug, "/files/etc/hosts/1/ipaddr", "127.0.0.2"); ++ CuAssertRetSuccess(tc, r); ++ ++ /* Check that we really did load the right file*/ ++ r = aug_load(aug); ++ CuAssertRetSuccess(tc, r); ++ ++ r = aug_get(aug, "/augeas/files/etc/hosts/mtime", &mtime2); ++ CuAssertIntEquals(tc, 1, r); ++ CuAssertStrNotEqual(tc, mtime1, mtime2); ++ ++ r = aug_match(aug, "/files/etc/hosts/*[ipaddr = '192.168.0.1']", NULL); ++ CuAssertIntEquals(tc, 1, r); ++ ++ r = aug_match(aug, "/files/etc/hosts/1[ipaddr = '127.0.0.1']", NULL); ++ CuAssertIntEquals(tc, 1, r); ++ ++ free(mtime1); ++ free(hosts); ++ aug_close(aug); ++} ++ ++static void testReloadDirty(CuTest *tc) { ++ augeas *aug = NULL; ++ int r; ++ ++ aug = setup_writable_hosts(tc); ++ ++ r = aug_load(aug); ++ CuAssertRetSuccess(tc, r); ++ ++ /* Unsaved changes are discarded */ ++ r = aug_set(aug, "/files/etc/hosts/1/ipaddr", "127.0.0.2"); ++ CuAssertRetSuccess(tc, r); ++ ++ r = aug_load(aug); ++ CuAssertRetSuccess(tc, r); ++ ++ r = aug_match(aug, "/files/etc/hosts/1[ipaddr = '127.0.0.1']", NULL); ++ CuAssertIntEquals(tc, 1, r); ++ ++ aug_close(aug); ++} ++ ++static void testReloadDeleted(CuTest *tc) { ++ augeas *aug = NULL; ++ int r; ++ ++ aug = setup_writable_hosts(tc); ++ ++ r = aug_load(aug); ++ CuAssertRetSuccess(tc, r); ++ ++ /* A missing file causes a reload */ ++ r = aug_rm(aug, "/files/etc/hosts"); ++ CuAssertPositive(tc, r); ++ ++ r = aug_load(aug); ++ CuAssertRetSuccess(tc, r); ++ ++ r = aug_match(aug, "/files/etc/hosts/1[ipaddr = '127.0.0.1']", NULL); ++ CuAssertIntEquals(tc, 1, r); ++ ++ /* A missing entry in a file causes a reload */ ++ r = aug_rm(aug, "/files/etc/hosts/1/ipaddr"); ++ CuAssertPositive(tc, r); ++ ++ r = aug_load(aug); ++ CuAssertRetSuccess(tc, r); ++ ++ r = aug_match(aug, "/files/etc/hosts/1[ipaddr = '127.0.0.1']", NULL); ++ CuAssertIntEquals(tc, 1, r); ++ ++ aug_close(aug); ++} ++ ++static void testReloadDeletedMeta(CuTest *tc) { ++ augeas *aug = NULL; ++ int r; ++ ++ aug = setup_writable_hosts(tc); ++ ++ r = aug_load(aug); ++ CuAssertRetSuccess(tc, r); ++ ++ /* Unsaved changes are discarded */ ++ r = aug_rm(aug, "/augeas/files/etc/hosts"); ++ CuAssertPositive(tc, r); ++ ++ r = aug_set(aug, "/files/etc/hosts/1/ipaddr", "127.0.0.2"); ++ CuAssertRetSuccess(tc, r); ++ ++ r = aug_load(aug); ++ CuAssertRetSuccess(tc, r); ++ ++ r = aug_match(aug, "/files/etc/hosts/1[ipaddr = '127.0.0.1']", NULL); ++ CuAssertIntEquals(tc, 1, r); ++ ++ aug_close(aug); ++} ++ + int main(void) { + char *output = NULL; + CuSuite* suite = CuSuiteNew(); +@@ -274,6 +412,10 @@ int main(void) { + SUITE_ADD_TEST(suite, testLoadSave); + SUITE_ADD_TEST(suite, testLoadDefined); + SUITE_ADD_TEST(suite, testDefvarExpr); ++ SUITE_ADD_TEST(suite, testReloadChanged); ++ SUITE_ADD_TEST(suite, testReloadDirty); ++ SUITE_ADD_TEST(suite, testReloadDeleted); ++ SUITE_ADD_TEST(suite, testReloadDeletedMeta); + + abs_top_srcdir = getenv("abs_top_srcdir"); + if (abs_top_srcdir == NULL) +diff --git a/tests/xpath.tests b/tests/xpath.tests +index f2575a3..b16792a 100644 +--- a/tests/xpath.tests ++++ b/tests/xpath.tests +@@ -148,6 +148,7 @@ test ipaddr-sibling //*[../ipaddr] + + test lircd-ancestor //*[ancestor::kudzu][label() != '#comment'] + /augeas/files/etc/sysconfig/kudzu/path = /files/etc/sysconfig/kudzu ++ /augeas/files/etc/sysconfig/kudzu/mtime = ... + /augeas/files/etc/sysconfig/kudzu/lens = @Shellvars + /augeas/files/etc/sysconfig/kudzu/lens/info = ... + /files/etc/sysconfig/kudzu/SAFE = no +-- +1.6.6.1 + diff --git a/augeas.spec b/augeas.spec index 201a089..b45edd0 100644 --- a/augeas.spec +++ b/augeas.spec @@ -1,12 +1,24 @@ Name: augeas Version: 0.7.2 -Release: 1%{?dist} +Release: 2%{?dist} Summary: A library for changing configuration files Group: System Environment/Libraries License: LGPLv2+ URL: http://augeas.net/ Source0: http://augeas.net/download/%{name}-%{version}.tar.gz +# Format of the patch name is augeas-VERSION-NUMBER-HASH where VERSION +# gives the first version where this patch was applied, NUMBER orders patches +# against the same version, and HASH is the git commit hash from upstream +Patch0: augeas-0.7.2-01-0c43ca0f.patch +Patch1: augeas-0.7.2-02-6c713a08.patch +Patch2: augeas-0.7.2-03-984ff1b8.patch +Patch3: augeas-0.7.2-04-218003a8.patch +Patch4: augeas-0.7.2-05-21d39d66.patch +Patch5: augeas-0.7.2-06-11b85805.patch +Patch6: augeas-0.7.2-07-72c636de.patch +Patch7: augeas-0.7.2-08-0695bdd0.patch +Patch8: augeas-0.7.2-09-5ee81630.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: readline-devel libselinux-devel @@ -43,6 +55,15 @@ The libraries for %{name}. %prep %setup -q +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 %build %configure --disable-static @@ -85,6 +106,9 @@ rm -rf $RPM_BUILD_ROOT %{_libdir}/pkgconfig/augeas.pc %changelog +* Tue Jun 29 2010 David Lutterkort - 0.7.2-2 +- Patches based on upstream fix for BZ 600141 + * Tue Jun 22 2010 David Lutterkort - 0.7.2-1 - Fix ownership of /usr/share/augeas. BZ 569393