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 occurred (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) {
552 make_lns_info(struct augeas *aug, const char *filename,
553 const char *text, int text_len) {
554 struct info *info = NULL;
557 ERR_NOMEM(info == NULL, aug);
559 if (filename != NULL) {
560 make_ref(info->filename);
561 ERR_NOMEM(info->filename == NULL, aug);
562 info->filename->str = strdup(filename);
565 info->first_line = 1;
567 info->first_column = 1;
569 info->last_column = text_len;
572 info->error = aug->error;
581 * Do the bookkeeping around calling lns_get that is common to load_file
582 * and text_store, in particular, make sure the tree we read gets put into
583 * the right place in AUG and that the span for that tree gets set.
585 * Transform TEXT using LENS and put the resulting tree at PATH. Use
586 * FILENAME in error messages to indicate where the TEXT came from.
588 static void lens_get(struct augeas *aug,
590 const char *filename,
591 const char *text, int text_len,
593 struct lns_error **err) {
594 struct info *info = NULL;
595 struct span *span = NULL;
596 struct tree *tree = NULL;
598 info = make_lns_info(aug, filename, text, text_len);
601 if (aug->flags & AUG_ENABLE_SPAN) {
602 /* Allocate the span already to capture a reference to
604 span = make_span(info);
605 ERR_NOMEM(span == NULL, info);
608 tree = lns_get(info, lens, text, aug->flags & AUG_ENABLE_SPAN, err);
612 tree_freplace(aug, path, tree);
615 /* top level node span entire file length */
616 if (span != NULL && tree != NULL) {
617 tree->parent->span = move(span);
618 tree->parent->span->span_start = 0;
619 tree->parent->span->span_end = text_len;
629 static int load_file(struct augeas *aug, struct lens *lens,
630 const char *lens_name, char *filename) {
632 const char *err_status = NULL;
634 struct lns_error *err = NULL;
635 int result = -1, r, text_len = 0;
637 path = file_name_path(aug, filename);
638 ERR_NOMEM(path == NULL, aug);
640 r = add_file_info(aug, path, lens, lens_name, filename, false);
644 text = xread_file(filename);
646 err_status = "read_failed";
649 text_len = strlen(text);
650 text = append_newline(text, text_len);
652 lens_get(aug, lens, filename, text, text_len, path, &err);
654 err_status = "parse_failed";
661 store_error(aug, filename + strlen(aug->root) - 1, path, err_status,
670 /* The lens for a transform can be referred to in one of two ways:
671 * either by a fully qualified name "Module.lens" or by the special
672 * syntax "@Module"; the latter means we should take the lens from the
673 * autoload transform for Module
675 static struct lens *lens_from_name(struct augeas *aug, const char *name) {
676 struct lens *result = NULL;
678 if (name[0] == '@') {
679 struct module *modl = NULL;
680 for (modl = aug->modules;
681 modl != NULL && !streqv(modl->name, name + 1);
683 ERR_THROW(modl == NULL, aug, AUG_ENOLENS,
684 "Could not find module %s", name + 1);
685 ERR_THROW(modl->autoload == NULL, aug, AUG_ENOLENS,
686 "No autoloaded lens in module %s", name + 1);
687 result = modl->autoload->lens;
689 result = lens_lookup(aug, name);
691 ERR_THROW(result == NULL, aug, AUG_ENOLENS,
692 "Can not find lens %s", name);
698 int text_store(struct augeas *aug, const char *lens_path,
699 const char *path, const char *text) {
700 struct lns_error *err = NULL;
702 const char *err_status = NULL;
703 struct lens *lens = NULL;
705 lens = lens_from_name(aug, lens_path);
708 lens_get(aug, lens, path, text, strlen(text), path, &err);
710 err_status = "parse_failed";
717 store_error(aug, NULL, path, err_status, errno, err, text);
722 const char *xfm_lens_name(struct tree *xfm) {
723 struct tree *l = tree_child(xfm, s_lens);
727 if (l->value == NULL)
732 struct lens *xfm_lens(struct augeas *aug,
733 struct tree *xfm, const char **lens_name) {
734 struct tree *l = NULL;
736 if (lens_name != NULL)
739 for (l = xfm->children;
740 l != NULL && !streqv("lens", l->label);
743 if (l == NULL || l->value == NULL)
745 if (lens_name != NULL)
746 *lens_name = l->value;
748 return lens_from_name(aug, l->value);
751 static void xfm_error(struct tree *xfm, const char *msg) {
752 char *v = msg ? strdup(msg) : NULL;
753 char *l = strdup("error");
755 if (l == NULL || v == NULL) {
760 tree_append(xfm, l, v);
763 int transform_validate(struct augeas *aug, struct tree *xfm) {
764 struct tree *l = NULL;
766 for (struct tree *t = xfm->children; t != NULL; ) {
767 if (streqv(t->label, "lens")) {
769 } else if ((is_incl(t) || (is_excl(t) && strchr(t->value, SEP) != NULL))
770 && t->value[0] != SEP) {
771 /* Normalize relative paths to absolute ones */
773 r = REALLOC_N(t->value, strlen(t->value) + 2);
774 ERR_NOMEM(r < 0, aug);
775 memmove(t->value + 1, t->value, strlen(t->value) + 1);
779 if (streqv(t->label, "error")) {
780 struct tree *del = t;
782 tree_unlink(aug, del);
789 xfm_error(xfm, "missing a child with label 'lens'");
792 if (l->value == NULL) {
793 xfm_error(xfm, "the 'lens' node does not contain a lens name");
796 lens_from_name(aug, l->value);
801 xfm_error(xfm, aug->error->details);
802 /* We recorded this error in the tree, clear it so that future
803 * operations report this exact same error (against the wrong lens) */
804 reset_error(aug->error);
808 void transform_file_error(struct augeas *aug, const char *status,
809 const char *filename, const char *format, ...) {
810 char *ep = err_path(filename);
816 err = tree_fpath_cr(aug, ep);
821 tree_unlink_children(aug, err);
822 tree_set_value(err, status);
824 err = tree_child_cr(err, s_message);
828 va_start(ap, format);
829 r = vasprintf(&msg, format, ap);
833 tree_set_value(err, msg);
837 static struct tree *file_info(struct augeas *aug, const char *fname) {
839 struct tree *result = NULL;
842 r = pathjoin(&path, 2, AUGEAS_META_FILES, fname);
843 ERR_NOMEM(r < 0, aug);
845 result = tree_fpath(aug, path);
852 int transform_load(struct augeas *aug, struct tree *xfm, const char *file) {
855 const char *lens_name;
856 struct lens *lens = xfm_lens(aug, xfm, &lens_name);
860 // FIXME: Record an error and return 0
864 r = filter_generate(xfm, aug->root, &nmatches, &matches);
867 for (int i=0; i < nmatches; i++) {
868 const char *filename = matches[i] + strlen(aug->root) - 1;
869 struct tree *finfo = file_info(aug, filename);
871 if (file != NULL && STRNEQ(filename, file)) {
876 if (finfo != NULL && !finfo->dirty &&
877 tree_child(finfo, s_lens) != NULL) {
878 /* We have a potential conflict: since FINFO is not marked as
879 dirty (see aug_load for how the dirty flag on nodes under
880 /augeas/files is used during loading), we already processed
881 it with another lens. The other lens is recorded in
882 FINFO. If it so happens that the lenses are actually the
883 same, we silently move on, as this duplication does no
884 harm. If they are different we definitely have a problem and
885 need to record an error and remove the work the first lens
887 const char *s = xfm_lens_name(finfo);
888 struct lens *other_lens = lens_from_name(aug, s);
889 if (lens != other_lens) {
890 char *fpath = file_name_path(aug, matches[i]);
891 transform_file_error(aug, "mxfm_load", filename,
892 "Lenses %s and %s could be used to load this file",
897 } else if (!file_current(aug, matches[i], finfo)) {
898 load_file(aug, lens, lens_name, matches[i]);
909 int transform_applies(struct tree *xfm, const char *path) {
910 if (STRNEQLEN(path, AUGEAS_FILES_TREE, strlen(AUGEAS_FILES_TREE))
911 || path[strlen(AUGEAS_FILES_TREE)] != SEP)
913 return filter_matches(xfm, path + strlen(AUGEAS_FILES_TREE));
916 static int transfer_file_attrs(FILE *from, FILE *to,
917 const char **err_status) {
920 int selinux_enabled = (is_selinux_enabled() > 0);
921 security_context_t con = NULL;
924 int to_fd = fileno(to);
927 *err_status = "replace_from_missing";
931 from_fd = fileno(from);
933 ret = fstat(from_fd, &st);
935 *err_status = "replace_stat";
938 if (selinux_enabled) {
939 if (fgetfilecon(from_fd, &con) < 0 && errno != ENOTSUP) {
940 *err_status = "replace_getfilecon";
945 if (fchown(to_fd, st.st_uid, st.st_gid) < 0) {
946 *err_status = "replace_chown";
949 if (fchmod(to_fd, st.st_mode) < 0) {
950 *err_status = "replace_chmod";
953 if (selinux_enabled && con != NULL) {
954 if (fsetfilecon(to_fd, con) < 0 && errno != ENOTSUP) {
955 *err_status = "replace_setfilecon";
963 /* Try to rename FROM to TO. If that fails with an error other than EXDEV
964 * or EBUSY, return -1. If the failure is EXDEV or EBUSY (which we assume
965 * means that FROM or TO is a bindmounted file), and COPY_IF_RENAME_FAILS
966 * is true, copy the contents of FROM into TO and delete FROM.
968 * If COPY_IF_RENAME_FAILS and UNLINK_IF_RENAME_FAILS are true, and the above
969 * copy mechanism is used, it will unlink the TO path and open with O_EXCL
970 * to ensure we only copy *from* a bind mount rather than into an attacker's
971 * mount placed at TO (e.g. for .augsave).
973 * Return 0 on success (either rename succeeded or we copied the contents
974 * over successfully), -1 on failure.
976 static int clone_file(const char *from, const char *to,
977 const char **err_status, int copy_if_rename_fails,
978 int unlink_if_rename_fails) {
979 FILE *from_fp = NULL, *to_fp = NULL;
982 int to_fd = -1, to_oflags, r;
985 if (rename(from, to) == 0)
987 if ((errno != EXDEV && errno != EBUSY) || !copy_if_rename_fails) {
988 *err_status = "rename";
992 /* rename not possible, copy file contents */
993 if (!(from_fp = fopen(from, "r"))) {
994 *err_status = "clone_open_src";
998 if (unlink_if_rename_fails) {
1001 *err_status = "clone_unlink_dst";
1006 to_oflags = unlink_if_rename_fails ? O_EXCL : O_TRUNC;
1007 if ((to_fd = open(to, O_WRONLY|O_CREAT|to_oflags, S_IRUSR|S_IWUSR)) < 0) {
1008 *err_status = "clone_open_dst";
1011 if (!(to_fp = fdopen(to_fd, "w"))) {
1012 *err_status = "clone_fdopen_dst";
1016 if (transfer_file_attrs(from_fp, to_fp, err_status) < 0)
1019 while ((len = fread(buf, 1, BUFSIZ, from_fp)) > 0) {
1020 if (fwrite(buf, 1, len, to_fp) != len) {
1021 *err_status = "clone_write";
1025 if (ferror(from_fp)) {
1026 *err_status = "clone_read";
1029 if (fflush(to_fp) != 0) {
1030 *err_status = "clone_flush";
1033 if (fsync(fileno(to_fp)) < 0) {
1034 *err_status = "clone_sync";
1039 if (from_fp != NULL)
1041 if (to_fp != NULL) {
1042 if (fclose(to_fp) != 0) {
1043 *err_status = "clone_fclose_dst";
1046 } else if (to_fd >= 0 && close(to_fd) < 0) {
1047 *err_status = "clone_close_dst";
1057 static char *strappend(const char *s1, const char *s2) {
1058 size_t len = strlen(s1) + strlen(s2);
1059 char *result = NULL, *p;
1061 if (ALLOC_N(result, len + 1) < 0)
1064 p = stpcpy(result, s1);
1069 static int file_saved_event(struct augeas *aug, const char *path) {
1070 const char *saved = strrchr(AUGEAS_EVENTS_SAVED, SEP) + 1;
1075 px = pathx_aug_parse(aug, aug->origin, NULL,
1076 AUGEAS_EVENTS_SAVED "[last()]", true);
1079 if (pathx_find_one(px, &dummy) == 1) {
1080 r = tree_insert(px, saved, 0);
1085 if (! tree_set(px, path))
1096 * Do the bookkeeping around calling LNS_PUT that's needed to update the
1097 * span after writing a tree to file
1099 static void lens_put(struct augeas *aug, const char *filename,
1100 struct lens *lens, const char *text, struct tree *tree,
1101 FILE *out, struct lns_error **err) {
1102 struct info *info = NULL;
1103 size_t text_len = strlen(text);
1104 bool with_span = aug->flags & AUG_ENABLE_SPAN;
1106 info = make_lns_info(aug, filename, text, text_len);
1110 if (tree->span == NULL) {
1111 tree->span = make_span(info);
1112 ERR_NOMEM(tree->span == NULL, aug);
1114 tree->span->span_start = ftell(out);
1117 lns_put(info, out, lens, tree->children, text,
1118 aug->flags & AUG_ENABLE_SPAN, err);
1121 tree->span->span_end = ftell(out);
1128 * Save TREE->CHILDREN into the file PATH using the lens from XFORM. Errors
1129 * are noted in the /augeas/files hierarchy in AUG->ORIGIN under
1132 * Writing the file happens by first writing into a temp file, transferring all
1133 * file attributes of PATH to the temp file, and then renaming the temp file
1136 * Temp files are created alongside the destination file to enable the rename,
1137 * which may be the canonical path (PATH_canon) if PATH is a symlink.
1139 * If the AUG_SAVE_NEWFILE flag is set, instead rename to PATH.augnew rather
1140 * than PATH. If AUG_SAVE_BACKUP is set, move the original to PATH.augsave.
1141 * (Always PATH.aug{new,save} irrespective of whether PATH is a symlink.)
1143 * If the rename fails, and the entry AUGEAS_COPY_IF_FAILURE exists in
1144 * AUG->ORIGIN, PATH is instead overwritten by copying file contents.
1146 * The table below shows the locations for each permutation.
1148 * PATH save flag temp file dest file backup?
1149 * regular - PATH.XXXX PATH -
1150 * regular BACKUP PATH.XXXX PATH PATH.augsave
1151 * regular NEWFILE PATH.augnew.XXXX PATH.augnew -
1152 * symlink - PATH_canon.XXXX PATH_canon -
1153 * symlink BACKUP PATH_canon.XXXX PATH_canon PATH.augsave
1154 * symlink NEWFILE PATH.augnew.XXXX PATH.augnew -
1156 * Return 0 on success, -1 on failure.
1158 int transform_save(struct augeas *aug, struct tree *xfm,
1159 const char *path, struct tree *tree) {
1161 FILE *fp = NULL, *augorig_canon_fp = NULL;
1162 char *augtemp = NULL, *augnew = NULL, *augorig = NULL, *augsave = NULL;
1163 char *augorig_canon = NULL, *augdest = NULL;
1165 int copy_if_rename_fails = 0;
1167 const char *filename = path + strlen(AUGEAS_FILES_TREE) + 1;
1168 const char *err_status = NULL;
1169 char *dyn_err_status = NULL;
1170 struct lns_error *err = NULL;
1171 const char *lens_name;
1172 struct lens *lens = xfm_lens(aug, xfm, &lens_name);
1175 struct info *info = NULL;
1180 err_status = "lens_name";
1184 copy_if_rename_fails =
1185 aug_get(aug, AUGEAS_COPY_IF_RENAME_FAILS, NULL) == 1;
1187 if (asprintf(&augorig, "%s%s", aug->root, filename) == -1) {
1192 augorig_canon = canonicalize_file_name(augorig);
1194 if (augorig_canon == NULL) {
1195 if (errno == ENOENT) {
1196 augorig_canon = augorig;
1199 err_status = "canon_augorig";
1204 if (access(augorig_canon, R_OK) == 0) {
1205 augorig_canon_fp = fopen(augorig_canon, "r");
1206 text = xfread_file(augorig_canon_fp);
1212 err_status = "put_read";
1216 text = append_newline(text, strlen(text));
1218 /* Figure out where to put the .augnew and temp file. If no .augnew file
1219 then put the temp file next to augorig_canon, else next to .augnew. */
1220 if (aug->flags & AUG_SAVE_NEWFILE) {
1221 if (xasprintf(&augnew, "%s" EXT_AUGNEW, augorig) < 0) {
1222 err_status = "augnew_oom";
1227 augdest = augorig_canon;
1230 if (xasprintf(&augtemp, "%s.XXXXXX", augdest) < 0) {
1231 err_status = "augtemp_oom";
1235 // FIXME: We might have to create intermediate directories
1236 // to be able to write augnew, but we have no idea what permissions
1237 // etc. they should get. Just the process default ?
1238 fd = mkstemp(augtemp);
1240 err_status = "mk_augtemp";
1243 fp = fdopen(fd, "w");
1245 err_status = "open_augtemp";
1249 if (augorig_exists) {
1250 if (transfer_file_attrs(augorig_canon_fp, fp, &err_status) != 0) {
1254 /* Since mkstemp is used, the temp file will have secure permissions
1255 * instead of those implied by umask, so change them for new files */
1256 mode_t curumsk = umask(022);
1259 if (fchmod(fileno(fp), 0666 & ~curumsk) < 0) {
1260 err_status = "create_chmod";
1266 lens_put(aug, augorig_canon, lens, text, tree, fp, &err);
1271 err_status = "error_augtemp";
1275 if (fflush(fp) != 0) {
1276 err_status = "flush_augtemp";
1280 if (fsync(fileno(fp)) < 0) {
1281 err_status = "sync_augtemp";
1285 if (fclose(fp) != 0) {
1286 err_status = "close_augtemp";
1294 err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
1300 char *new_text = xread_file(augtemp);
1302 if (new_text == NULL) {
1303 err_status = "read_augtemp";
1306 same = STREQ(text, new_text);
1312 } else if (aug->flags & AUG_SAVE_NOOP) {
1319 if (!(aug->flags & AUG_SAVE_NEWFILE)) {
1320 if (augorig_exists && (aug->flags & AUG_SAVE_BACKUP)) {
1321 r = xasprintf(&augsave, "%s" EXT_AUGSAVE, augorig);
1327 r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
1329 dyn_err_status = strappend(err_status, "_augsave");
1335 r = clone_file(augtemp, augdest, &err_status, copy_if_rename_fails, 0);
1338 dyn_err_status = strappend(err_status, "_augtemp");
1345 force_reload = aug->flags & AUG_SAVE_NEWFILE;
1346 r = add_file_info(aug, path, lens, lens_name, augorig, force_reload);
1348 err_status = "file_info";
1352 r = file_saved_event(aug, path);
1354 err_status = "saved_event";
1360 dyn_err_status == NULL ? err_status : dyn_err_status;
1361 store_error(aug, filename, path, emsg, errno, err, text);
1364 free(dyn_err_status);
1369 if (augorig_canon != augorig)
1370 free(augorig_canon);
1373 free_lns_error(err);
1378 if (augorig_canon_fp != NULL)
1379 fclose(augorig_canon_fp);
1383 int text_retrieve(struct augeas *aug, const char *lens_name,
1384 const char *path, struct tree *tree,
1385 const char *text_in, char **text_out) {
1386 struct memstream ms;
1387 bool ms_open = false;
1388 const char *err_status = NULL;
1389 struct lns_error *err = NULL;
1390 struct lens *lens = NULL;
1392 struct info *info = NULL;
1397 lens = lens_from_name(aug, lens_name);
1399 err_status = "lens_name";
1403 r = init_memstream(&ms);
1405 err_status = "init_memstream";
1411 lens_put(aug, path, lens, text_in, tree, ms.stream, &err);
1415 r = close_memstream(&ms);
1418 err_status = "close_memstream";
1426 err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
1433 store_error(aug, NULL, path, err_status, errno, err, text_in);
1440 free_lns_error(err);
1444 close_memstream(&ms);
1448 int remove_file(struct augeas *aug, struct tree *tree) {
1449 const char *err_status = NULL;
1450 char *dyn_err_status = NULL;
1451 char *augsave = NULL, *augorig = NULL, *augorig_canon = NULL;
1452 struct tree *path = NULL;
1453 const char *file_path = NULL;
1454 char *meta_path = NULL;
1457 path = tree_child(tree, s_path);
1459 err_status = "no child called 'path' for file entry";
1462 file_path = path->value + strlen(AUGEAS_FILES_TREE);
1465 meta_path = path_of_tree(tree);
1466 if (meta_path == NULL) {
1467 err_status = "path_of_tree";
1471 if ((augorig = strappend(aug->root, file_path)) == NULL) {
1472 err_status = "root_file";
1476 augorig_canon = canonicalize_file_name(augorig);
1477 if (augorig_canon == NULL) {
1478 if (errno == ENOENT) {
1481 err_status = "canon_augorig";
1486 r = file_saved_event(aug, meta_path + strlen(AUGEAS_META_TREE));
1488 err_status = "saved_event";
1492 if (aug->flags & AUG_SAVE_NOOP)
1495 if (aug->flags & AUG_SAVE_BACKUP) {
1496 /* Move file to one with extension .augsave */
1497 r = asprintf(&augsave, "%s" EXT_AUGSAVE, augorig_canon);
1503 r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
1505 dyn_err_status = strappend(err_status, "_augsave");
1510 r = unlink(augorig_canon);
1512 err_status = "unlink_orig";
1517 tree_unlink(aug, tree);
1521 free(augorig_canon);
1527 dyn_err_status == NULL ? err_status : dyn_err_status;
1528 store_error(aug, file_path, meta_path, emsg, errno, NULL, NULL);
1532 free(augorig_canon);
1534 free(dyn_err_status);
1540 * indent-tabs-mode: nil