Address BZ 600141 with upstream patches

This commit is contained in:
David Lutterkort 2010-06-30 01:31:11 +00:00
parent b27d156edf
commit c7935c3ce8
10 changed files with 1002 additions and 1 deletions

View File

@ -0,0 +1,33 @@
From 0c43ca0f0ce9008a991c2a2d99e938f4a60712a3 Mon Sep 17 00:00:00 2001
From: David Lutterkort <lutter@redhat.com>
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

View File

@ -0,0 +1,28 @@
From 6c713a0855f6f2f144ebc2240fc4ec0e3470aacc Mon Sep 17 00:00:00 2001
From: David Lutterkort <lutter@redhat.com>
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

View File

@ -0,0 +1,56 @@
From 984ff1b8f37c64339ed9663d2b052dadbea82032 Mon Sep 17 00:00:00 2001
From: David Lutterkort <lutter@redhat.com>
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

View File

@ -0,0 +1,96 @@
From 218003a813acae99370d45157fe57589b8a8685c Mon Sep 17 00:00:00 2001
From: David Lutterkort <lutter@redhat.com>
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

View File

@ -0,0 +1,98 @@
From 21d39d66b9c2d827cbede4ace1fe0fc0b637992b Mon Sep 17 00:00:00 2001
From: David Lutterkort <lutter@redhat.com>
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

View File

@ -0,0 +1,55 @@
From 11b85805955557ae87494fff57ead04be6ab90b6 Mon Sep 17 00:00:00 2001
From: David Lutterkort <lutter@redhat.com>
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

View File

@ -0,0 +1,42 @@
From 72c636decaa8b8c1764710c60f9932892c411207 Mon Sep 17 00:00:00 2001
From: David Lutterkort <lutter@redhat.com>
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

View File

@ -0,0 +1,68 @@
From 0695bdd057b2cbfc6146ac498179c0f162ee889f Mon Sep 17 00:00:00 2001
From: David Lutterkort <lutter@redhat.com>
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

View File

@ -0,0 +1,501 @@
From 5ee8163051be8214507c13c86171ac90ca7cb91f Mon Sep 17 00:00:00 2001
From: David Lutterkort <lutter@redhat.com>
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

View File

@ -1,12 +1,24 @@
Name: augeas Name: augeas
Version: 0.7.2 Version: 0.7.2
Release: 1%{?dist} Release: 2%{?dist}
Summary: A library for changing configuration files Summary: A library for changing configuration files
Group: System Environment/Libraries Group: System Environment/Libraries
License: LGPLv2+ License: LGPLv2+
URL: http://augeas.net/ URL: http://augeas.net/
Source0: http://augeas.net/download/%{name}-%{version}.tar.gz 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) BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildRequires: readline-devel libselinux-devel BuildRequires: readline-devel libselinux-devel
@ -43,6 +55,15 @@ The libraries for %{name}.
%prep %prep
%setup -q %setup -q
%patch0 -p1
%patch1 -p1
%patch2 -p1
%patch3 -p1
%patch4 -p1
%patch5 -p1
%patch6 -p1
%patch7 -p1
%patch8 -p1
%build %build
%configure --disable-static %configure --disable-static
@ -85,6 +106,9 @@ rm -rf $RPM_BUILD_ROOT
%{_libdir}/pkgconfig/augeas.pc %{_libdir}/pkgconfig/augeas.pc
%changelog %changelog
* Tue Jun 29 2010 David Lutterkort <lutter@redhat.com> - 0.7.2-2
- Patches based on upstream fix for BZ 600141
* Tue Jun 22 2010 David Lutterkort <lutter@redhat.com> - 0.7.2-1 * Tue Jun 22 2010 David Lutterkort <lutter@redhat.com> - 0.7.2-1
- Fix ownership of /usr/share/augeas. BZ 569393 - Fix ownership of /usr/share/augeas. BZ 569393