Binární soubory augeas-0.9.0.old/src/.augeas.c.swp a augeas-0.9.0/src/.augeas.c.swp jsou rozdílné
diff -urN augeas-0.9.0.old/src/internal.c augeas-0.9.0/src/internal.c
--- augeas-0.9.0.old/src/internal.c	2011-07-25 23:34:14.000000000 +0200
+++ augeas-0.9.0/src/internal.c	2014-07-22 11:19:38.002758595 +0200
@@ -125,8 +125,7 @@
     return NULL;
 }
 
-char* xread_file(const char *path) {
-    FILE *fp = fopen(path, "r");
+char* xfread_file(FILE *fp) {
     char *result;
     size_t len;
 
@@ -134,7 +133,6 @@
         return NULL;
 
     result = fread_file_lim(fp, MAX_READ_LEN, &len);
-    fclose (fp);
 
     if (result != NULL
         && len <= MAX_READ_LEN
@@ -145,6 +143,17 @@
     return NULL;
 }
 
+char* xread_file(const char *path) {
+    FILE *fp;
+    char *result;
+
+    fp = fopen(path, "r");
+    result = xfread_file(fp);
+    fclose (fp);
+
+    return result;
+}
+
 /*
  * Escape/unescape of string literals
  */
diff -urN augeas-0.9.0.old/src/internal.h augeas-0.9.0/src/internal.h
--- augeas-0.9.0.old/src/internal.h	2011-07-25 23:34:14.000000000 +0200
+++ augeas-0.9.0/src/internal.h	2014-07-22 11:19:38.002758595 +0200
@@ -272,6 +272,9 @@
  */
 char* xread_file(const char *path);
 
+/* Like xread_file, but caller supplies a file pointer */
+char* xfread_file(FILE *fp);
+
 /* Get the error message for ERRNUM in a threadsafe way. Based on libvirt's
  * virStrError
  */
diff -urN augeas-0.9.0.old/src/transform.c augeas-0.9.0/src/transform.c
--- augeas-0.9.0.old/src/transform.c	2014-07-22 12:56:24.821295552 +0200
+++ augeas-0.9.0/src/transform.c	2014-07-22 13:37:43.131524721 +0200
@@ -480,7 +480,6 @@
                      const char *lens_name, char *filename) {
     char *text = NULL;
     const char *err_status = NULL;
-    struct aug_file *file = NULL;
     struct tree *tree = NULL;
     char *path = NULL;
     struct lns_error *err = NULL;
@@ -527,7 +526,7 @@
     tree_replace(aug, path, tree);
 
     /* top level node span entire file length */
-    if (span != NULL) {
+    if (span != NULL && tree != NULL) {
         tree->parent->span = span;
         tree->parent->span->span_start = 0;
         tree->parent->span->span_end = text_len;
@@ -543,7 +542,6 @@
     free_lns_error(err);
     free(path);
     free_tree(tree);
-    free(file);
     free(text);
     return result;
 }
@@ -729,35 +727,38 @@
     return filter_matches(xfm, path + strlen(AUGEAS_FILES_TREE));
 }
 
-static int transfer_file_attrs(const char *from, const char *to,
+static int transfer_file_attrs(FILE *from, FILE *to,
                                const char **err_status) {
     struct stat st;
     int ret = 0;
     int selinux_enabled = (is_selinux_enabled() > 0);
     security_context_t con = NULL;
 
-    ret = lstat(from, &st);
+    int from_fd = fileno(from);
+    int to_fd = fileno(to);
+
+    ret = fstat(from_fd, &st);
     if (ret < 0) {
         *err_status = "replace_stat";
         return -1;
     }
     if (selinux_enabled) {
-        if (lgetfilecon(from, &con) < 0 && errno != ENOTSUP) {
+        if (fgetfilecon(from_fd, &con) < 0 && errno != ENOTSUP) {
             *err_status = "replace_getfilecon";
             return -1;
         }
     }
 
-    if (lchown(to, st.st_uid, st.st_gid) < 0) {
+    if (fchown(to_fd, st.st_uid, st.st_gid) < 0) {
         *err_status = "replace_chown";
         return -1;
     }
-    if (chmod(to, st.st_mode) < 0) {
+    if (fchmod(to_fd, st.st_mode) < 0) {
         *err_status = "replace_chmod";
         return -1;
     }
     if (selinux_enabled && con != NULL) {
-        if (lsetfilecon(to, con) < 0 && errno != ENOTSUP) {
+        if (fsetfilecon(to_fd, con) < 0 && errno != ENOTSUP) {
             *err_status = "replace_setfilecon";
             return -1;
         }
@@ -789,7 +790,6 @@
     }
 
     /* rename not possible, copy file contents */
-    from_fp = fopen(from, "r");
     if (!(from_fp = fopen(from, "r"))) {
         *err_status = "clone_open_src";
         goto done;
@@ -800,7 +800,7 @@
         goto done;
     }
 
-    if (transfer_file_attrs(from, to, err_status) < 0)
+    if (transfer_file_attrs(from_fp, to_fp, err_status) < 0)
         goto done;
 
     while ((len = fread(buf, 1, BUFSIZ, from_fp)) > 0) {
@@ -877,19 +877,38 @@
  * are noted in the /augeas/files hierarchy in AUG->ORIGIN under
  * PATH/error.
  *
- * Writing the file happens by first writing into PATH.augnew, transferring
- * all file attributes of PATH to PATH.augnew, and then renaming
- * PATH.augnew to PATH. If the rename fails, and the entry
- * AUGEAS_COPY_IF_FAILURE exists in AUG->ORIGIN, PATH is overwritten by
- * copying file contents
+ * Writing the file happens by first writing into a temp file, transferring all
+ * file attributes of PATH to the temp file, and then renaming the temp file
+ * back to PATH.
+ *
+ * Temp files are created alongside the destination file to enable the rename,
+ * which may be the canonical path (PATH_canon) if PATH is a symlink.
+ *
+ * If the AUG_SAVE_NEWFILE flag is set, instead rename to PATH.augnew rather
+ * than PATH.  If AUG_SAVE_BACKUP is set, move the original to PATH.augsave.
+ * (Always PATH.aug{new,save} irrespective of whether PATH is a symlink.)
+ *
+ * If the rename fails, and the entry AUGEAS_COPY_IF_FAILURE exists in
+ * AUG->ORIGIN, PATH is instead overwritten by copying file contents.
+ *
+ * The table below shows the locations for each permutation.
+ *
+ * PATH       save flag    temp file           dest file      backup?
+ * regular    -            PATH.XXXX           PATH           -
+ * regular    BACKUP       PATH.XXXX           PATH           PATH.augsave
+ * regular    NEWFILE      PATH.augnew.XXXX    PATH.augnew    -
+ * symlink    -            PATH_canon.XXXX     PATH_canon     -
+ * symlink    BACKUP       PATH_canon.XXXX     PATH_canon     PATH.augsave
+ * symlink    NEWFILE      PATH.augnew.XXXX    PATH.augnew    -
  *
  * Return 0 on success, -1 on failure.
  */
 int transform_save(struct augeas *aug, struct tree *xfm,
                    const char *path, struct tree *tree) {
-    FILE *fp = NULL;
-    char *augnew = NULL, *augorig = NULL, *augsave = NULL;
-    char *augorig_canon = NULL;
+    int   fd;
+    FILE *fp = NULL, *augorig_canon_fp = NULL;
+    char *augtemp = NULL, *augnew = NULL, *augorig = NULL, *augsave = NULL;
+    char *augorig_canon = NULL, *augdest = NULL;
     int   augorig_exists;
     int   copy_if_rename_fails = 0;
     char *text = NULL;
@@ -916,19 +935,6 @@
         goto done;
     }
 
-    if (access(augorig, R_OK) == 0) {
-        text = xread_file(augorig);
-    } else {
-        text = strdup("");
-    }
-
-    if (text == NULL) {
-        err_status = "put_read";
-        goto done;
-    }
-
-    text = append_newline(text, strlen(text));
-
     augorig_canon = canonicalize_file_name(augorig);
     augorig_exists = 1;
     if (augorig_canon == NULL) {
@@ -941,31 +947,53 @@
         }
     }
 
-    /* Figure out where to put the .augnew file. If we need to rename it
-       later on, put it next to augorig_canon */
+    if (access(augorig_canon, R_OK) == 0) {
+        augorig_canon_fp = fopen(augorig_canon, "r");
+        text = xfread_file(augorig_canon_fp);
+    } else {
+        text = strdup("");
+    }
+
+    if (text == NULL) {
+        err_status = "put_read";
+        goto done;
+    }
+
+    text = append_newline(text, strlen(text));
+
+    /* Figure out where to put the .augnew and temp file. If no .augnew file
+       then put the temp file next to augorig_canon, else next to .augnew. */
     if (aug->flags & AUG_SAVE_NEWFILE) {
         if (xasprintf(&augnew, "%s" EXT_AUGNEW, augorig) < 0) {
             err_status = "augnew_oom";
             goto done;
         }
+        augdest = augnew;
     } else {
-        if (xasprintf(&augnew, "%s" EXT_AUGNEW, augorig_canon) < 0) {
-            err_status = "augnew_oom";
-            goto done;
-        }
+        augdest = augorig_canon;
+    }
+
+    if (xasprintf(&augtemp, "%s.XXXXXX", augdest) < 0) {
+        err_status = "augtemp_oom";
+        goto done;
     }
 
     // FIXME: We might have to create intermediate directories
     // to be able to write augnew, but we have no idea what permissions
     // etc. they should get. Just the process default ?
-    fp = fopen(augnew, "w");
+    fd = mkstemp(augtemp);
+    if (fd < 0) {
+        err_status = "mk_augtemp";
+        goto done;
+    }
+    fp = fdopen(fd, "w");
     if (fp == NULL) {
-        err_status = "open_augnew";
+        err_status = "open_augtemp";
         goto done;
     }
 
     if (augorig_exists) {
-        if (transfer_file_attrs(augorig_canon, augnew, &err_status) != 0) {
+        if (transfer_file_attrs(augorig_canon_fp, fp, &err_status) != 0) {
             err_status = "xfer_attrs";
             goto done;
         }
@@ -975,56 +1003,56 @@
         lns_put(fp, lens, tree->children, text, &err);
 
     if (ferror(fp)) {
-        err_status = "error_augnew";
+        err_status = "error_augtemp";
         goto done;
     }
 
     if (fflush(fp) != 0) {
-        err_status = "flush_augnew";
+        err_status = "flush_augtemp";
         goto done;
     }
 
     if (fsync(fileno(fp)) < 0) {
-        err_status = "sync_augnew";
+        err_status = "sync_augtemp";
         goto done;
     }
 
     if (fclose(fp) != 0) {
-        err_status = "close_augnew";
+        err_status = "close_augtemp";
         goto done;
     }
 
     fp = NULL;
 
     if (err != NULL) {
-        err_status = "put_failed";
-        unlink(augnew);
+    	err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
+        unlink(augtemp);
         goto done;
     }
 
     {
-        char *new_text = xread_file(augnew);
+        char *new_text = xread_file(augtemp);
         int same = 0;
         if (new_text == NULL) {
-            err_status = "read_augnew";
+            err_status = "read_augtemp";
             goto done;
         }
         same = STREQ(text, new_text);
         FREE(new_text);
         if (same) {
             result = 0;
-            unlink(augnew);
+            unlink(augtemp);
             goto done;
         } else if (aug->flags & AUG_SAVE_NOOP) {
             result = 1;
-            unlink(augnew);
+            unlink(augtemp);
             goto done;
         }
     }
 
     if (!(aug->flags & AUG_SAVE_NEWFILE)) {
         if (augorig_exists && (aug->flags & AUG_SAVE_BACKUP)) {
-            r = asprintf(&augsave, "%s%s" EXT_AUGSAVE, aug->root, filename);
+            r = xasprintf(&augsave, "%s" EXT_AUGSAVE, augorig);
             if (r == -1) {
                 augsave = NULL;
                 goto done;
@@ -1036,13 +1064,14 @@
                 goto done;
             }
         }
-        r = clone_file(augnew, augorig_canon, &err_status,
-                       copy_if_rename_fails);
-        if (r != 0) {
-            dyn_err_status = strappend(err_status, "_augnew");
-            goto done;
-        }
     }
+
+    r = clone_file(augtemp, augdest, &err_status, copy_if_rename_fails);
+    if (r != 0) {
+        dyn_err_status = strappend(err_status, "_augtemp");
+        goto done;
+    }
+
     result = 1;
 
  done:
@@ -1061,11 +1090,12 @@
     {
         const char *emsg =
             dyn_err_status == NULL ? err_status : dyn_err_status;
-        store_error(aug, filename, path, emsg, errno, err, NULL);
+        store_error(aug, filename, path, emsg, errno, err, text);
     }
     free(dyn_err_status);
     lens_release(lens);
     free(text);
+    free(augtemp);
     free(augnew);
     if (augorig_canon != augorig)
         free(augorig_canon);
@@ -1075,6 +1105,9 @@
 
     if (fp != NULL)
         fclose(fp);
+	fp = NULL;
+    if (augorig_canon_fp != NULL)
+        fclose(augorig_canon_fp);
     return result;
 }
 
diff -urN augeas-0.9.0.old/tests/test-put-symlink.sh augeas-0.9.0/tests/test-put-symlink.sh
--- augeas-0.9.0.old/tests/test-put-symlink.sh	2010-10-15 00:26:08.000000000 +0200
+++ augeas-0.9.0/tests/test-put-symlink.sh	2014-07-22 11:22:53.675776689 +0200
@@ -41,3 +41,8 @@
     echo "/etc/hosts does not link to ../other/hosts"
     exit 1
 fi
+
+if ! grep myhost $REAL_HOSTS >/dev/null; then
+    echo "/other/hosts does not contain the modification"
+    exit 1
+fi
diff -urN augeas-0.9.0.old/tests/test-save-empty.sh augeas-0.9.0/tests/test-save-empty.sh
--- augeas-0.9.0.old/tests/test-save-empty.sh	2010-10-15 00:26:08.000000000 +0200
+++ augeas-0.9.0/tests/test-save-empty.sh	2014-07-22 11:20:27.805763200 +0200
@@ -15,7 +15,7 @@
 
 expected_errors() {
 cat <<EOF
-/augeas/files/etc/hosts/error = "open_augnew"
+/augeas/files/etc/hosts/error = "mk_augtemp"
 /augeas/files/etc/hosts/error/message = "No such file or directory"
 EOF
 }
@@ -31,6 +31,7 @@
 if [ "$ACTUAL" != "$EXPECTED" ]
 then
     echo "No error on missing /etc directory"
+    echo "$ACTUAL"
     exit 1
 fi
 
