2 * transform.c: support for building and running transformers
4 * Copyright (C) 2007-2016 David Lutterkort
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Author: David Lutterkort <dlutter@redhat.com>
28 #include <sys/types.h>
32 #include <selinux/selinux.h>
39 #include "transform.h"
42 static const int fnm_flags = FNM_PATHNAME;
43 static const int glob_flags = GLOB_NOSORT;
45 /* Extension for newly created files */
46 #define EXT_AUGNEW ".augnew"
47 /* Extension for backup files */
48 #define EXT_AUGSAVE ".augsave"
50 /* Loaded files are tracked underneath METATREE. When a file with name
51 * FNAME is loaded, certain entries are made under METATREE / FNAME:
52 * path : path where tree for FNAME is put
53 * mtime : time of last modification of the file as reported by stat(2)
54 * lens/info : information about where the applied lens was loaded from
55 * lens/id : unique hexadecimal id of the lens
56 * error : indication of errors during processing FNAME, or NULL
57 * if processing succeeded
58 * error/pos : position in file where error occured (for get errors)
59 * error/path: path to tree node where error occurred (for put errors)
60 * error/message : human-readable error message
62 static const char *const s_path = "path";
63 static const char *const s_lens = "lens";
64 static const char *const s_last = "last_matched";
65 static const char *const s_next = "next_not_matched";
66 static const char *const s_info = "info";
67 static const char *const s_mtime = "mtime";
69 static const char *const s_error = "error";
70 /* These are all put underneath "error" */
71 static const char *const s_pos = "pos";
72 static const char *const s_message = "message";
73 static const char *const s_line = "line";
74 static const char *const s_char = "char";
79 struct filter *make_filter(struct string *glb, unsigned int include) {
87 void free_filter(struct filter *f) {
91 unref(f->next, filter);
92 unref(f->glob, string);
96 static const char *pathbase(const char *path) {
97 const char *p = strrchr(path, SEP);
98 return (p == NULL) ? path : p + 1;
101 static bool is_excl(struct tree *f) {
102 return streqv(f->label, "excl") && f->value != NULL;
105 static bool is_incl(struct tree *f) {
106 return streqv(f->label, "incl") && f->value != NULL;
109 static bool is_regular_file(const char *path) {
116 return S_ISREG(st.st_mode);
119 static char *mtime_as_string(struct augeas *aug, const char *fname) {
125 result = strdup("0");
126 ERR_NOMEM(result == NULL, aug);
130 r = stat(fname, &st);
132 /* If we fail to stat, silently ignore the error
133 * and report an impossible mtime */
134 result = strdup("0");
135 ERR_NOMEM(result == NULL, aug);
137 r = xasprintf(&result, "%ld", (long) st.st_mtime);
138 ERR_NOMEM(r < 0, aug);
147 /* fnmatch(3) which will match // in a pattern to a path, like glob(3) does */
148 static int fnmatch_normalize(const char *pattern, const char *string, int flags) {
150 char *pattern_norm = NULL;
152 r = ALLOC_N(pattern_norm, strlen(pattern) + 1);
156 for (i = 0, j = 0; i < strlen(pattern); i++) {
157 if (pattern[i] != '/' || pattern[i+1] != '/') {
158 pattern_norm[j] = pattern[i];
164 r = fnmatch(pattern_norm, string, flags);
169 if (pattern_norm != NULL)
174 static bool file_current(struct augeas *aug, const char *fname,
175 struct tree *finfo) {
176 struct tree *mtime = tree_child(finfo, s_mtime);
177 struct tree *file = NULL, *path = NULL;
182 if (mtime == NULL || mtime->value == NULL)
185 r = xstrtoint64(mtime->value, 10, &mtime_i);
187 /* Ignore silently and err on the side of caution */
191 r = stat(fname, &st);
195 if (mtime_i != (int64_t) st.st_mtime)
198 path = tree_child(finfo, s_path);
202 file = tree_fpath(aug, path->value);
203 return (file != NULL && ! file->dirty);
206 static int filter_generate(struct tree *xfm, const char *root,
207 int *nmatches, char ***matches) {
209 int gl_flags = glob_flags;
214 int root_prefix = strlen(root) - 1;
218 MEMZERO(&globbuf, 1);
220 list_for_each(f, xfm->children) {
221 char *globpat = NULL;
224 pathjoin(&globpat, 2, root, f->value);
225 r = glob(globpat, gl_flags, NULL, &globbuf);
228 if (r != 0 && r != GLOB_NOMATCH)
230 gl_flags |= GLOB_APPEND;
233 pathc = globbuf.gl_pathc;
236 if (ALLOC_N(pathv, pathc) < 0)
239 for (int i=0; i < pathc; i++) {
240 const char *path = globbuf.gl_pathv[i] + root_prefix;
243 list_for_each(e, xfm->children) {
247 if (strchr(e->value, SEP) == NULL)
248 path = pathbase(path);
250 r = fnmatch_normalize(e->value, path, fnm_flags);
258 include = is_regular_file(globbuf.gl_pathv[i]);
261 pathv[pathind] = strdup(globbuf.gl_pathv[i]);
262 if (pathv[pathind] == NULL)
269 if (REALLOC_N(pathv, pathc) == -1)
279 for (int i=0; i < pathc; i++)
286 int filter_matches(struct tree *xfm, const char *path) {
288 list_for_each(f, xfm->children) {
289 if (is_incl(f) && fnmatch_normalize(f->value, path, fnm_flags) == 0) {
296 list_for_each(f, xfm->children) {
297 if (is_excl(f) && (fnmatch_normalize(f->value, path, fnm_flags) == 0))
306 struct transform *make_transform(struct lens *lens, struct filter *filter) {
307 struct transform *xform;
310 xform->filter = filter;
314 void free_transform(struct transform *xform) {
317 assert(xform->ref == 0);
318 unref(xform->lens, lens);
319 unref(xform->filter, filter);
323 static char *err_path(const char *filename) {
325 if (filename == NULL)
326 pathjoin(&result, 2, AUGEAS_META_FILES, s_error);
328 pathjoin(&result, 3, AUGEAS_META_FILES, filename, s_error);
332 ATTRIBUTE_FORMAT(printf, 4, 5)
333 static struct tree *err_set(struct augeas *aug,
334 struct tree *err_info, const char *sub,
335 const char *format, ...) {
339 struct tree *tree = NULL;
341 va_start(ap, format);
342 r = vasprintf(&value, format, ap);
346 ERR_NOMEM(r < 0, aug);
348 tree = tree_child_cr(err_info, sub);
349 ERR_NOMEM(tree == NULL, aug);
351 r = tree_set_value(tree, value);
352 ERR_NOMEM(r < 0, aug);
359 static struct tree *err_lens_entry(struct augeas *aug, struct tree *where,
360 struct lens *lens, const char *label) {
361 struct tree *result = NULL;
366 char *fi = format_info(lens->info);
368 result = err_set(aug, where, label, "%s", fi);
374 /* Record an error in the tree. The error will show up underneath
375 * /augeas/FILENAME/error if filename is not NULL, and underneath
376 * /augeas/text/PATH otherwise. PATH is the path to the toplevel node in
377 * the tree where the lens application happened. When STATUS is NULL, just
378 * clear any error associated with FILENAME in the tree.
380 static int store_error(struct augeas *aug,
381 const char *filename, const char *path,
382 const char *status, int errnum,
383 const struct lns_error *err, const char *text) {
384 struct tree *err_info = NULL, *finfo = NULL;
389 if (filename != NULL) {
390 r = pathjoin(&fip, 2, AUGEAS_META_FILES, filename);
392 r = pathjoin(&fip, 2, AUGEAS_META_TEXT, path);
394 ERR_NOMEM(r < 0, aug);
396 finfo = tree_fpath_cr(aug, fip);
399 if (status != NULL) {
400 err_info = tree_child_cr(finfo, s_error);
401 ERR_NOMEM(err_info == NULL, aug);
403 r = tree_set_value(err_info, status);
404 ERR_NOMEM(r < 0, aug);
406 /* Errors from err_set are ignored on purpose. We try
407 * to report as much as we can */
411 err_set(aug, err_info, s_pos, "%d", err->pos);
413 calc_line_ofs(text, err->pos, &line, &ofs);
414 err_set(aug, err_info, s_line, "%zd", line);
415 err_set(aug, err_info, s_char, "%zd", ofs);
418 if (err->path != NULL) {
419 err_set(aug, err_info, s_path, "%s%s", path, err->path);
421 struct tree *t = err_lens_entry(aug, err_info, err->lens, s_lens);
423 err_lens_entry(aug, t, err->last, s_last);
424 err_lens_entry(aug, t, err->next, s_next);
426 err_set(aug, err_info, s_message, "%s", err->message);
427 } else if (errnum != 0) {
428 const char *msg = strerror(errnum);
429 err_set(aug, err_info, s_message, "%s", msg);
432 /* No error, nuke the error node if it exists */
433 err_info = tree_child(finfo, s_error);
434 if (err_info != NULL)
435 tree_unlink(aug, err_info);
445 /* Set up the file information in the /augeas tree.
447 * NODE must be the path to the file contents, and start with /files.
448 * LENS is the lens used to transform the file.
449 * Create entries under /augeas/NODE with some metadata about the file.
451 * Returns 0 on success, -1 on error
453 static int add_file_info(struct augeas *aug, const char *node,
454 struct lens *lens, const char *lens_name,
455 const char *filename, bool force_reload) {
456 struct tree *file, *tree;
465 r = pathjoin(&path, 2, AUGEAS_META_TREE, node);
466 ERR_NOMEM(r < 0, aug);
468 file = tree_fpath_cr(aug, path);
473 tree = tree_child_cr(file, s_path);
474 ERR_NOMEM(tree == NULL, aug);
475 r = tree_set_value(tree, node);
476 ERR_NOMEM(r < 0, aug);
481 ERR_NOMEM(tmp == NULL, aug);
483 tmp = mtime_as_string(aug, filename);
486 tree = tree_child_cr(file, s_mtime);
487 ERR_NOMEM(tree == NULL, aug);
488 tree_store_value(tree, &tmp);
490 /* Set 'lens/info' */
491 tmp = format_info(lens->info);
492 ERR_NOMEM(tmp == NULL, aug);
493 tree = tree_path_cr(file, 2, s_lens, s_info);
494 ERR_NOMEM(tree == NULL, aug);
495 r = tree_set_value(tree, tmp);
496 ERR_NOMEM(r < 0, aug);
501 r = tree_set_value(tree, lens_name);
502 ERR_NOMEM(r < 0, aug);
513 static char *append_newline(char *text, size_t len) {
514 /* Try to append a newline; this is a big hack to work */
515 /* around the fact that lenses generally break if the */
516 /* file does not end with a newline. */
517 if (len == 0 || text[len-1] != '\n') {
518 if (REALLOC_N(text, len+2) == 0) {
526 /* Turn the file name FNAME, which starts with aug->root, into
527 * a path in the tree underneath /files */
528 static char *file_name_path(struct augeas *aug, const char *fname) {
531 pathjoin(&path, 2, AUGEAS_FILES_TREE, fname + strlen(aug->root) - 1);
535 /* Replace the subtree for FPATH with SUB */
536 static void tree_freplace(struct augeas *aug, const char *fpath,
540 parent = tree_fpath_cr(aug, fpath);
544 tree_unlink_children(aug, parent);
545 list_append(parent->children, sub);
546 list_for_each(s, sub) {
551 static int load_file(struct augeas *aug, struct lens *lens,
552 const char *lens_name, char *filename) {
554 const char *err_status = NULL;
555 struct tree *tree = NULL;
557 struct lns_error *err = NULL;
558 struct span *span = NULL;
559 int result = -1, r, text_len = 0;
560 struct info *info = NULL;
562 path = file_name_path(aug, filename);
563 ERR_NOMEM(path == NULL, aug);
565 r = add_file_info(aug, path, lens, lens_name, filename, false);
569 text = xread_file(filename);
571 err_status = "read_failed";
574 text_len = strlen(text);
575 text = append_newline(text, text_len);
578 make_ref(info->filename);
579 info->filename->str = strdup(filename);
580 info->error = aug->error;
581 info->flags = aug->flags;
582 info->first_line = 1;
584 if (aug->flags & AUG_ENABLE_SPAN) {
585 /* Allocate the span already to capture a reference to
587 span = make_span(info);
588 ERR_NOMEM(span == NULL, info);
591 tree = lns_get(info, lens, text, &err);
594 err_status = "parse_failed";
598 tree_freplace(aug, path, tree);
601 /* top level node span entire file length */
602 if (span != NULL && tree != NULL) {
603 tree->parent->span = span;
605 tree->parent->span->span_start = 0;
606 tree->parent->span->span_end = text_len;
613 store_error(aug, filename + strlen(aug->root) - 1, path, err_status,
625 /* The lens for a transform can be referred to in one of two ways:
626 * either by a fully qualified name "Module.lens" or by the special
627 * syntax "@Module"; the latter means we should take the lens from the
628 * autoload transform for Module
630 static struct lens *lens_from_name(struct augeas *aug, const char *name) {
631 struct lens *result = NULL;
633 if (name[0] == '@') {
634 struct module *modl = NULL;
635 for (modl = aug->modules;
636 modl != NULL && !streqv(modl->name, name + 1);
638 ERR_THROW(modl == NULL, aug, AUG_ENOLENS,
639 "Could not find module %s", name + 1);
640 ERR_THROW(modl->autoload == NULL, aug, AUG_ENOLENS,
641 "No autoloaded lens in module %s", name + 1);
642 result = modl->autoload->lens;
644 result = lens_lookup(aug, name);
646 ERR_THROW(result == NULL, aug, AUG_ENOLENS,
647 "Can not find lens %s", name);
653 int text_store(struct augeas *aug, const char *lens_path,
654 const char *path, const char *text) {
655 struct info *info = NULL;
656 struct lns_error *err = NULL;
657 struct tree *tree = NULL;
659 const char *err_status = NULL;
660 struct lens *lens = NULL;
662 lens = lens_from_name(aug, lens_path);
666 info->first_line = 1;
668 info->first_column = 1;
669 info->last_column = strlen(text);
671 tree = lns_get(info, lens, text, &err);
673 err_status = "parse_failed";
677 tree_freplace(aug, path, tree);
685 store_error(aug, NULL, path, err_status, errno, err, text);
691 const char *xfm_lens_name(struct tree *xfm) {
692 struct tree *l = tree_child(xfm, s_lens);
696 if (l->value == NULL)
701 struct lens *xfm_lens(struct augeas *aug,
702 struct tree *xfm, const char **lens_name) {
703 struct tree *l = NULL;
705 for (l = xfm->children;
706 l != NULL && !streqv("lens", l->label);
709 if (l == NULL || l->value == NULL)
711 if (lens_name != NULL)
712 *lens_name = l->value;
714 return lens_from_name(aug, l->value);
717 static void xfm_error(struct tree *xfm, const char *msg) {
718 char *v = msg ? strdup(msg) : NULL;
719 char *l = strdup("error");
721 if (l == NULL || v == NULL) {
726 tree_append(xfm, l, v);
729 int transform_validate(struct augeas *aug, struct tree *xfm) {
730 struct tree *l = NULL;
732 for (struct tree *t = xfm->children; t != NULL; ) {
733 if (streqv(t->label, "lens")) {
735 } else if ((is_incl(t) || (is_excl(t) && strchr(t->value, SEP) != NULL))
736 && t->value[0] != SEP) {
737 /* Normalize relative paths to absolute ones */
739 r = REALLOC_N(t->value, strlen(t->value) + 2);
740 ERR_NOMEM(r < 0, aug);
741 memmove(t->value + 1, t->value, strlen(t->value) + 1);
745 if (streqv(t->label, "error")) {
746 struct tree *del = t;
748 tree_unlink(aug, del);
755 xfm_error(xfm, "missing a child with label 'lens'");
758 if (l->value == NULL) {
759 xfm_error(xfm, "the 'lens' node does not contain a lens name");
762 lens_from_name(aug, l->value);
767 xfm_error(xfm, aug->error->details);
771 void transform_file_error(struct augeas *aug, const char *status,
772 const char *filename, const char *format, ...) {
773 char *ep = err_path(filename);
779 err = tree_fpath_cr(aug, ep);
784 tree_unlink_children(aug, err);
785 tree_set_value(err, status);
787 err = tree_child_cr(err, s_message);
791 va_start(ap, format);
792 r = vasprintf(&msg, format, ap);
796 tree_set_value(err, msg);
800 static struct tree *file_info(struct augeas *aug, const char *fname) {
802 struct tree *result = NULL;
805 r = pathjoin(&path, 2, AUGEAS_META_FILES, fname);
806 ERR_NOMEM(r < 0, aug);
808 result = tree_fpath(aug, path);
815 int transform_load(struct augeas *aug, struct tree *xfm, const char *file) {
818 const char *lens_name;
819 struct lens *lens = xfm_lens(aug, xfm, &lens_name);
823 // FIXME: Record an error and return 0
827 r = filter_generate(xfm, aug->root, &nmatches, &matches);
830 for (int i=0; i < nmatches; i++) {
831 const char *filename = matches[i] + strlen(aug->root) - 1;
832 struct tree *finfo = file_info(aug, filename);
834 if (file != NULL && STRNEQ(filename, file)) {
839 if (finfo != NULL && !finfo->dirty &&
840 tree_child(finfo, s_lens) != NULL) {
841 /* We have a potential conflict: since FINFO is not marked as
842 dirty (see aug_load for how the dirty flag on nodes under
843 /augeas/files is used during loading), we already processed
844 it with another lens. The other lens is recorded in
845 FINFO. If it so happens that the lenses are actually the
846 same, we silently move on, as this duplication does no
847 harm. If they are different we definitely have a problem and
848 need to record an error and remove the work the first lens
850 const char *s = xfm_lens_name(finfo);
851 struct lens *other_lens = lens_from_name(aug, s);
852 if (lens != other_lens) {
853 char *fpath = file_name_path(aug, matches[i]);
854 transform_file_error(aug, "mxfm_load", filename,
855 "Lenses %s and %s could be used to load this file",
860 } else if (!file_current(aug, matches[i], finfo)) {
861 load_file(aug, lens, lens_name, matches[i]);
872 int transform_applies(struct tree *xfm, const char *path) {
873 if (STRNEQLEN(path, AUGEAS_FILES_TREE, strlen(AUGEAS_FILES_TREE))
874 || path[strlen(AUGEAS_FILES_TREE)] != SEP)
876 return filter_matches(xfm, path + strlen(AUGEAS_FILES_TREE));
879 static int transfer_file_attrs(FILE *from, FILE *to,
880 const char **err_status) {
883 int selinux_enabled = (is_selinux_enabled() > 0);
884 security_context_t con = NULL;
887 int to_fd = fileno(to);
890 *err_status = "replace_from_missing";
894 from_fd = fileno(from);
896 ret = fstat(from_fd, &st);
898 *err_status = "replace_stat";
901 if (selinux_enabled) {
902 if (fgetfilecon(from_fd, &con) < 0 && errno != ENOTSUP) {
903 *err_status = "replace_getfilecon";
908 if (fchown(to_fd, st.st_uid, st.st_gid) < 0) {
909 *err_status = "replace_chown";
912 if (fchmod(to_fd, st.st_mode) < 0) {
913 *err_status = "replace_chmod";
916 if (selinux_enabled && con != NULL) {
917 if (fsetfilecon(to_fd, con) < 0 && errno != ENOTSUP) {
918 *err_status = "replace_setfilecon";
926 /* Try to rename FROM to TO. If that fails with an error other than EXDEV
927 * or EBUSY, return -1. If the failure is EXDEV or EBUSY (which we assume
928 * means that FROM or TO is a bindmounted file), and COPY_IF_RENAME_FAILS
929 * is true, copy the contents of FROM into TO and delete FROM.
931 * If COPY_IF_RENAME_FAILS and UNLINK_IF_RENAME_FAILS are true, and the above
932 * copy mechanism is used, it will unlink the TO path and open with O_EXCL
933 * to ensure we only copy *from* a bind mount rather than into an attacker's
934 * mount placed at TO (e.g. for .augsave).
936 * Return 0 on success (either rename succeeded or we copied the contents
937 * over successfully), -1 on failure.
939 static int clone_file(const char *from, const char *to,
940 const char **err_status, int copy_if_rename_fails,
941 int unlink_if_rename_fails) {
942 FILE *from_fp = NULL, *to_fp = NULL;
945 int to_fd = -1, to_oflags, r;
948 if (rename(from, to) == 0)
950 if ((errno != EXDEV && errno != EBUSY) || !copy_if_rename_fails) {
951 *err_status = "rename";
955 /* rename not possible, copy file contents */
956 if (!(from_fp = fopen(from, "r"))) {
957 *err_status = "clone_open_src";
961 if (unlink_if_rename_fails) {
964 *err_status = "clone_unlink_dst";
969 to_oflags = unlink_if_rename_fails ? O_EXCL : O_TRUNC;
970 if ((to_fd = open(to, O_WRONLY|O_CREAT|to_oflags, S_IRUSR|S_IWUSR)) < 0) {
971 *err_status = "clone_open_dst";
974 if (!(to_fp = fdopen(to_fd, "w"))) {
975 *err_status = "clone_fdopen_dst";
979 if (transfer_file_attrs(from_fp, to_fp, err_status) < 0)
982 while ((len = fread(buf, 1, BUFSIZ, from_fp)) > 0) {
983 if (fwrite(buf, 1, len, to_fp) != len) {
984 *err_status = "clone_write";
988 if (ferror(from_fp)) {
989 *err_status = "clone_read";
992 if (fflush(to_fp) != 0) {
993 *err_status = "clone_flush";
996 if (fsync(fileno(to_fp)) < 0) {
997 *err_status = "clone_sync";
1002 if (from_fp != NULL)
1004 if (to_fp != NULL) {
1005 if (fclose(to_fp) != 0) {
1006 *err_status = "clone_fclose_dst";
1009 } else if (to_fd >= 0 && close(to_fd) < 0) {
1010 *err_status = "clone_close_dst";
1020 static char *strappend(const char *s1, const char *s2) {
1021 size_t len = strlen(s1) + strlen(s2);
1022 char *result = NULL, *p;
1024 if (ALLOC_N(result, len + 1) < 0)
1027 p = stpcpy(result, s1);
1032 static int file_saved_event(struct augeas *aug, const char *path) {
1033 const char *saved = strrchr(AUGEAS_EVENTS_SAVED, SEP) + 1;
1038 px = pathx_aug_parse(aug, aug->origin, NULL,
1039 AUGEAS_EVENTS_SAVED "[last()]", true);
1042 if (pathx_find_one(px, &dummy) == 1) {
1043 r = tree_insert(px, saved, 0);
1048 if (! tree_set(px, path))
1059 * Save TREE->CHILDREN into the file PATH using the lens from XFORM. Errors
1060 * are noted in the /augeas/files hierarchy in AUG->ORIGIN under
1063 * Writing the file happens by first writing into a temp file, transferring all
1064 * file attributes of PATH to the temp file, and then renaming the temp file
1067 * Temp files are created alongside the destination file to enable the rename,
1068 * which may be the canonical path (PATH_canon) if PATH is a symlink.
1070 * If the AUG_SAVE_NEWFILE flag is set, instead rename to PATH.augnew rather
1071 * than PATH. If AUG_SAVE_BACKUP is set, move the original to PATH.augsave.
1072 * (Always PATH.aug{new,save} irrespective of whether PATH is a symlink.)
1074 * If the rename fails, and the entry AUGEAS_COPY_IF_FAILURE exists in
1075 * AUG->ORIGIN, PATH is instead overwritten by copying file contents.
1077 * The table below shows the locations for each permutation.
1079 * PATH save flag temp file dest file backup?
1080 * regular - PATH.XXXX PATH -
1081 * regular BACKUP PATH.XXXX PATH PATH.augsave
1082 * regular NEWFILE PATH.augnew.XXXX PATH.augnew -
1083 * symlink - PATH_canon.XXXX PATH_canon -
1084 * symlink BACKUP PATH_canon.XXXX PATH_canon PATH.augsave
1085 * symlink NEWFILE PATH.augnew.XXXX PATH.augnew -
1087 * Return 0 on success, -1 on failure.
1089 int transform_save(struct augeas *aug, struct tree *xfm,
1090 const char *path, struct tree *tree) {
1092 FILE *fp = NULL, *augorig_canon_fp = NULL;
1093 char *augtemp = NULL, *augnew = NULL, *augorig = NULL, *augsave = NULL;
1094 char *augorig_canon = NULL, *augdest = NULL;
1096 int copy_if_rename_fails = 0;
1098 const char *filename = path + strlen(AUGEAS_FILES_TREE) + 1;
1099 const char *err_status = NULL;
1100 char *dyn_err_status = NULL;
1101 struct lns_error *err = NULL;
1102 const char *lens_name;
1103 struct lens *lens = xfm_lens(aug, xfm, &lens_name);
1110 err_status = "lens_name";
1114 copy_if_rename_fails =
1115 aug_get(aug, AUGEAS_COPY_IF_RENAME_FAILS, NULL) == 1;
1117 if (asprintf(&augorig, "%s%s", aug->root, filename) == -1) {
1122 augorig_canon = canonicalize_file_name(augorig);
1124 if (augorig_canon == NULL) {
1125 if (errno == ENOENT) {
1126 augorig_canon = augorig;
1129 err_status = "canon_augorig";
1134 if (access(augorig_canon, R_OK) == 0) {
1135 augorig_canon_fp = fopen(augorig_canon, "r");
1136 text = xfread_file(augorig_canon_fp);
1142 err_status = "put_read";
1146 text = append_newline(text, strlen(text));
1148 /* Figure out where to put the .augnew and temp file. If no .augnew file
1149 then put the temp file next to augorig_canon, else next to .augnew. */
1150 if (aug->flags & AUG_SAVE_NEWFILE) {
1151 if (xasprintf(&augnew, "%s" EXT_AUGNEW, augorig) < 0) {
1152 err_status = "augnew_oom";
1157 augdest = augorig_canon;
1160 if (xasprintf(&augtemp, "%s.XXXXXX", augdest) < 0) {
1161 err_status = "augtemp_oom";
1165 // FIXME: We might have to create intermediate directories
1166 // to be able to write augnew, but we have no idea what permissions
1167 // etc. they should get. Just the process default ?
1168 fd = mkstemp(augtemp);
1170 err_status = "mk_augtemp";
1173 fp = fdopen(fd, "w");
1175 err_status = "open_augtemp";
1179 if (augorig_exists) {
1180 if (transfer_file_attrs(augorig_canon_fp, fp, &err_status) != 0) {
1184 /* Since mkstemp is used, the temp file will have secure permissions
1185 * instead of those implied by umask, so change them for new files */
1186 mode_t curumsk = umask(022);
1189 if (fchmod(fileno(fp), 0666 & ~curumsk) < 0) {
1190 err_status = "create_chmod";
1196 lns_put(fp, lens, tree->children, text, &err);
1199 err_status = "error_augtemp";
1203 if (fflush(fp) != 0) {
1204 err_status = "flush_augtemp";
1208 if (fsync(fileno(fp)) < 0) {
1209 err_status = "sync_augtemp";
1213 if (fclose(fp) != 0) {
1214 err_status = "close_augtemp";
1222 err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
1228 char *new_text = xread_file(augtemp);
1230 if (new_text == NULL) {
1231 err_status = "read_augtemp";
1234 same = STREQ(text, new_text);
1240 } else if (aug->flags & AUG_SAVE_NOOP) {
1247 if (!(aug->flags & AUG_SAVE_NEWFILE)) {
1248 if (augorig_exists && (aug->flags & AUG_SAVE_BACKUP)) {
1249 r = xasprintf(&augsave, "%s" EXT_AUGSAVE, augorig);
1255 r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
1257 dyn_err_status = strappend(err_status, "_augsave");
1263 r = clone_file(augtemp, augdest, &err_status, copy_if_rename_fails, 0);
1265 dyn_err_status = strappend(err_status, "_augtemp");
1272 force_reload = aug->flags & AUG_SAVE_NEWFILE;
1273 r = add_file_info(aug, path, lens, lens_name, augorig, force_reload);
1275 err_status = "file_info";
1279 r = file_saved_event(aug, path);
1281 err_status = "saved_event";
1287 dyn_err_status == NULL ? err_status : dyn_err_status;
1288 store_error(aug, filename, path, emsg, errno, err, text);
1290 free(dyn_err_status);
1295 if (augorig_canon != augorig)
1296 free(augorig_canon);
1299 free_lns_error(err);
1303 if (augorig_canon_fp != NULL)
1304 fclose(augorig_canon_fp);
1308 int text_retrieve(struct augeas *aug, const char *lens_name,
1309 const char *path, struct tree *tree,
1310 const char *text_in, char **text_out) {
1311 struct memstream ms;
1312 bool ms_open = false;
1313 const char *err_status = NULL;
1314 struct lns_error *err = NULL;
1315 struct lens *lens = NULL;
1321 lens = lens_from_name(aug, lens_name);
1323 err_status = "lens_name";
1327 r = init_memstream(&ms);
1329 err_status = "init_memstream";
1335 lns_put(ms.stream, lens, tree->children, text_in, &err);
1337 r = close_memstream(&ms);
1340 err_status = "close_memstream";
1348 err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
1355 store_error(aug, NULL, path, err_status, errno, err, text_in);
1361 free_lns_error(err);
1364 close_memstream(&ms);
1368 int remove_file(struct augeas *aug, struct tree *tree) {
1369 const char *err_status = NULL;
1370 char *dyn_err_status = NULL;
1371 char *augsave = NULL, *augorig = NULL, *augorig_canon = NULL;
1372 struct tree *path = NULL;
1373 const char *file_path = NULL;
1374 char *meta_path = NULL;
1377 path = tree_child(tree, s_path);
1379 err_status = "no child called 'path' for file entry";
1382 file_path = path->value + strlen(AUGEAS_FILES_TREE);
1385 meta_path = path_of_tree(tree);
1386 if (meta_path == NULL) {
1387 err_status = "path_of_tree";
1391 if ((augorig = strappend(aug->root, file_path)) == NULL) {
1392 err_status = "root_file";
1396 augorig_canon = canonicalize_file_name(augorig);
1397 if (augorig_canon == NULL) {
1398 if (errno == ENOENT) {
1401 err_status = "canon_augorig";
1406 r = file_saved_event(aug, meta_path + strlen(AUGEAS_META_TREE));
1408 err_status = "saved_event";
1412 if (aug->flags & AUG_SAVE_NOOP)
1415 if (aug->flags & AUG_SAVE_BACKUP) {
1416 /* Move file to one with extension .augsave */
1417 r = asprintf(&augsave, "%s" EXT_AUGSAVE, augorig_canon);
1423 r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
1425 dyn_err_status = strappend(err_status, "_augsave");
1430 r = unlink(augorig_canon);
1432 err_status = "unlink_orig";
1437 tree_unlink(aug, tree);
1441 free(augorig_canon);
1447 dyn_err_status == NULL ? err_status : dyn_err_status;
1448 store_error(aug, file_path, meta_path, emsg, errno, NULL, NULL);
1452 free(augorig_canon);
1454 free(dyn_err_status);
1460 * indent-tabs-mode: nil