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);
543 tree_unlink_children(aug, parent);
544 list_append(parent->children, sub);
545 list_for_each(s, sub) {
550 static int load_file(struct augeas *aug, struct lens *lens,
551 const char *lens_name, char *filename) {
553 const char *err_status = NULL;
554 struct tree *tree = NULL;
556 struct lns_error *err = NULL;
557 struct span *span = NULL;
558 int result = -1, r, text_len = 0;
559 struct info *info = NULL;
561 path = file_name_path(aug, filename);
562 ERR_NOMEM(path == NULL, aug);
564 r = add_file_info(aug, path, lens, lens_name, filename, false);
568 text = xread_file(filename);
570 err_status = "read_failed";
573 text_len = strlen(text);
574 text = append_newline(text, text_len);
577 make_ref(info->filename);
578 info->filename->str = strdup(filename);
579 info->error = aug->error;
580 info->flags = aug->flags;
581 info->first_line = 1;
583 if (aug->flags & AUG_ENABLE_SPAN) {
584 /* Allocate the span already to capture a reference to
586 span = make_span(info);
587 ERR_NOMEM(span == NULL, info);
590 tree = lns_get(info, lens, text, &err);
593 err_status = "parse_failed";
597 tree_freplace(aug, path, tree);
600 /* top level node span entire file length */
601 if (span != NULL && tree != NULL) {
602 tree->parent->span = span;
604 tree->parent->span->span_start = 0;
605 tree->parent->span->span_end = text_len;
612 store_error(aug, filename + strlen(aug->root) - 1, path, err_status,
624 /* The lens for a transform can be referred to in one of two ways:
625 * either by a fully qualified name "Module.lens" or by the special
626 * syntax "@Module"; the latter means we should take the lens from the
627 * autoload transform for Module
629 static struct lens *lens_from_name(struct augeas *aug, const char *name) {
630 struct lens *result = NULL;
632 if (name[0] == '@') {
633 struct module *modl = NULL;
634 for (modl = aug->modules;
635 modl != NULL && !streqv(modl->name, name + 1);
637 ERR_THROW(modl == NULL, aug, AUG_ENOLENS,
638 "Could not find module %s", name + 1);
639 ERR_THROW(modl->autoload == NULL, aug, AUG_ENOLENS,
640 "No autoloaded lens in module %s", name + 1);
641 result = modl->autoload->lens;
643 result = lens_lookup(aug, name);
645 ERR_THROW(result == NULL, aug, AUG_ENOLENS,
646 "Can not find lens %s", name);
652 int text_store(struct augeas *aug, const char *lens_path,
653 const char *path, const char *text) {
654 struct info *info = NULL;
655 struct lns_error *err = NULL;
656 struct tree *tree = NULL;
658 const char *err_status = NULL;
659 struct lens *lens = NULL;
661 lens = lens_from_name(aug, lens_path);
665 info->first_line = 1;
667 info->first_column = 1;
668 info->last_column = strlen(text);
670 tree = lns_get(info, lens, text, &err);
672 err_status = "parse_failed";
676 tree_freplace(aug, path, tree);
684 store_error(aug, NULL, path, err_status, errno, err, text);
690 const char *xfm_lens_name(struct tree *xfm) {
691 struct tree *l = tree_child(xfm, s_lens);
695 if (l->value == NULL)
700 struct lens *xfm_lens(struct augeas *aug,
701 struct tree *xfm, const char **lens_name) {
702 struct tree *l = NULL;
704 for (l = xfm->children;
705 l != NULL && !streqv("lens", l->label);
708 if (l == NULL || l->value == NULL)
710 if (lens_name != NULL)
711 *lens_name = l->value;
713 return lens_from_name(aug, l->value);
716 static void xfm_error(struct tree *xfm, const char *msg) {
717 char *v = msg ? strdup(msg) : NULL;
718 char *l = strdup("error");
720 if (l == NULL || v == NULL) {
725 tree_append(xfm, l, v);
728 int transform_validate(struct augeas *aug, struct tree *xfm) {
729 struct tree *l = NULL;
731 for (struct tree *t = xfm->children; t != NULL; ) {
732 if (streqv(t->label, "lens")) {
734 } else if ((is_incl(t) || (is_excl(t) && strchr(t->value, SEP) != NULL))
735 && t->value[0] != SEP) {
736 /* Normalize relative paths to absolute ones */
738 r = REALLOC_N(t->value, strlen(t->value) + 2);
739 ERR_NOMEM(r < 0, aug);
740 memmove(t->value + 1, t->value, strlen(t->value) + 1);
744 if (streqv(t->label, "error")) {
745 struct tree *del = t;
747 tree_unlink(aug, del);
754 xfm_error(xfm, "missing a child with label 'lens'");
757 if (l->value == NULL) {
758 xfm_error(xfm, "the 'lens' node does not contain a lens name");
761 lens_from_name(aug, l->value);
766 xfm_error(xfm, aug->error->details);
770 void transform_file_error(struct augeas *aug, const char *status,
771 const char *filename, const char *format, ...) {
772 char *ep = err_path(filename);
778 err = tree_fpath_cr(aug, ep);
783 tree_unlink_children(aug, err);
784 tree_set_value(err, status);
786 err = tree_child_cr(err, s_message);
790 va_start(ap, format);
791 r = vasprintf(&msg, format, ap);
795 tree_set_value(err, msg);
799 static struct tree *file_info(struct augeas *aug, const char *fname) {
801 struct tree *result = NULL;
804 r = pathjoin(&path, 2, AUGEAS_META_FILES, fname);
805 ERR_NOMEM(r < 0, aug);
807 result = tree_fpath(aug, path);
814 int transform_load(struct augeas *aug, struct tree *xfm, const char *file) {
817 const char *lens_name;
818 struct lens *lens = xfm_lens(aug, xfm, &lens_name);
822 // FIXME: Record an error and return 0
826 r = filter_generate(xfm, aug->root, &nmatches, &matches);
829 for (int i=0; i < nmatches; i++) {
830 const char *filename = matches[i] + strlen(aug->root) - 1;
831 struct tree *finfo = file_info(aug, filename);
833 if (file != NULL && STRNEQ(filename, file)) {
838 if (finfo != NULL && !finfo->dirty &&
839 tree_child(finfo, s_lens) != NULL) {
840 /* We have a potential conflict: since FINFO is not marked as
841 dirty (see aug_load for how the dirty flag on nodes under
842 /augeas/files is used during loading), we already processed
843 it with another lens. The other lens is recorded in
844 FINFO. If it so happens that the lenses are actually the
845 same, we silently move on, as this duplication does no
846 harm. If they are different we definitely have a problem and
847 need to record an error and remove the work the first lens
849 const char *s = xfm_lens_name(finfo);
850 struct lens *other_lens = lens_from_name(aug, s);
851 if (lens != other_lens) {
852 char *fpath = file_name_path(aug, matches[i]);
853 transform_file_error(aug, "mxfm_load", filename,
854 "Lenses %s and %s could be used to load this file",
859 } else if (!file_current(aug, matches[i], finfo)) {
860 load_file(aug, lens, lens_name, matches[i]);
871 int transform_applies(struct tree *xfm, const char *path) {
872 if (STRNEQLEN(path, AUGEAS_FILES_TREE, strlen(AUGEAS_FILES_TREE))
873 || path[strlen(AUGEAS_FILES_TREE)] != SEP)
875 return filter_matches(xfm, path + strlen(AUGEAS_FILES_TREE));
878 static int transfer_file_attrs(FILE *from, FILE *to,
879 const char **err_status) {
882 int selinux_enabled = (is_selinux_enabled() > 0);
883 security_context_t con = NULL;
886 int to_fd = fileno(to);
889 *err_status = "replace_from_missing";
893 from_fd = fileno(from);
895 ret = fstat(from_fd, &st);
897 *err_status = "replace_stat";
900 if (selinux_enabled) {
901 if (fgetfilecon(from_fd, &con) < 0 && errno != ENOTSUP) {
902 *err_status = "replace_getfilecon";
907 if (fchown(to_fd, st.st_uid, st.st_gid) < 0) {
908 *err_status = "replace_chown";
911 if (fchmod(to_fd, st.st_mode) < 0) {
912 *err_status = "replace_chmod";
915 if (selinux_enabled && con != NULL) {
916 if (fsetfilecon(to_fd, con) < 0 && errno != ENOTSUP) {
917 *err_status = "replace_setfilecon";
925 /* Try to rename FROM to TO. If that fails with an error other than EXDEV
926 * or EBUSY, return -1. If the failure is EXDEV or EBUSY (which we assume
927 * means that FROM or TO is a bindmounted file), and COPY_IF_RENAME_FAILS
928 * is true, copy the contents of FROM into TO and delete FROM.
930 * If COPY_IF_RENAME_FAILS and UNLINK_IF_RENAME_FAILS are true, and the above
931 * copy mechanism is used, it will unlink the TO path and open with O_EXCL
932 * to ensure we only copy *from* a bind mount rather than into an attacker's
933 * mount placed at TO (e.g. for .augsave).
935 * Return 0 on success (either rename succeeded or we copied the contents
936 * over successfully), -1 on failure.
938 static int clone_file(const char *from, const char *to,
939 const char **err_status, int copy_if_rename_fails,
940 int unlink_if_rename_fails) {
941 FILE *from_fp = NULL, *to_fp = NULL;
944 int to_fd = -1, to_oflags, r;
947 if (rename(from, to) == 0)
949 if ((errno != EXDEV && errno != EBUSY) || !copy_if_rename_fails) {
950 *err_status = "rename";
954 /* rename not possible, copy file contents */
955 if (!(from_fp = fopen(from, "r"))) {
956 *err_status = "clone_open_src";
960 if (unlink_if_rename_fails) {
963 *err_status = "clone_unlink_dst";
968 to_oflags = unlink_if_rename_fails ? O_EXCL : O_TRUNC;
969 if ((to_fd = open(to, O_WRONLY|O_CREAT|to_oflags, S_IRUSR|S_IWUSR)) < 0) {
970 *err_status = "clone_open_dst";
973 if (!(to_fp = fdopen(to_fd, "w"))) {
974 *err_status = "clone_fdopen_dst";
978 if (transfer_file_attrs(from_fp, to_fp, err_status) < 0)
981 while ((len = fread(buf, 1, BUFSIZ, from_fp)) > 0) {
982 if (fwrite(buf, 1, len, to_fp) != len) {
983 *err_status = "clone_write";
987 if (ferror(from_fp)) {
988 *err_status = "clone_read";
991 if (fflush(to_fp) != 0) {
992 *err_status = "clone_flush";
995 if (fsync(fileno(to_fp)) < 0) {
996 *err_status = "clone_sync";
1001 if (from_fp != NULL)
1003 if (to_fp != NULL) {
1004 if (fclose(to_fp) != 0) {
1005 *err_status = "clone_fclose_dst";
1008 } else if (to_fd >= 0 && close(to_fd) < 0) {
1009 *err_status = "clone_close_dst";
1019 static char *strappend(const char *s1, const char *s2) {
1020 size_t len = strlen(s1) + strlen(s2);
1021 char *result = NULL, *p;
1023 if (ALLOC_N(result, len + 1) < 0)
1026 p = stpcpy(result, s1);
1031 static int file_saved_event(struct augeas *aug, const char *path) {
1032 const char *saved = strrchr(AUGEAS_EVENTS_SAVED, SEP) + 1;
1037 px = pathx_aug_parse(aug, aug->origin, NULL,
1038 AUGEAS_EVENTS_SAVED "[last()]", true);
1041 if (pathx_find_one(px, &dummy) == 1) {
1042 r = tree_insert(px, saved, 0);
1047 if (! tree_set(px, path))
1058 * Save TREE->CHILDREN into the file PATH using the lens from XFORM. Errors
1059 * are noted in the /augeas/files hierarchy in AUG->ORIGIN under
1062 * Writing the file happens by first writing into a temp file, transferring all
1063 * file attributes of PATH to the temp file, and then renaming the temp file
1066 * Temp files are created alongside the destination file to enable the rename,
1067 * which may be the canonical path (PATH_canon) if PATH is a symlink.
1069 * If the AUG_SAVE_NEWFILE flag is set, instead rename to PATH.augnew rather
1070 * than PATH. If AUG_SAVE_BACKUP is set, move the original to PATH.augsave.
1071 * (Always PATH.aug{new,save} irrespective of whether PATH is a symlink.)
1073 * If the rename fails, and the entry AUGEAS_COPY_IF_FAILURE exists in
1074 * AUG->ORIGIN, PATH is instead overwritten by copying file contents.
1076 * The table below shows the locations for each permutation.
1078 * PATH save flag temp file dest file backup?
1079 * regular - PATH.XXXX PATH -
1080 * regular BACKUP PATH.XXXX PATH PATH.augsave
1081 * regular NEWFILE PATH.augnew.XXXX PATH.augnew -
1082 * symlink - PATH_canon.XXXX PATH_canon -
1083 * symlink BACKUP PATH_canon.XXXX PATH_canon PATH.augsave
1084 * symlink NEWFILE PATH.augnew.XXXX PATH.augnew -
1086 * Return 0 on success, -1 on failure.
1088 int transform_save(struct augeas *aug, struct tree *xfm,
1089 const char *path, struct tree *tree) {
1091 FILE *fp = NULL, *augorig_canon_fp = NULL;
1092 char *augtemp = NULL, *augnew = NULL, *augorig = NULL, *augsave = NULL;
1093 char *augorig_canon = NULL, *augdest = NULL;
1095 int copy_if_rename_fails = 0;
1097 const char *filename = path + strlen(AUGEAS_FILES_TREE) + 1;
1098 const char *err_status = NULL;
1099 char *dyn_err_status = NULL;
1100 struct lns_error *err = NULL;
1101 const char *lens_name;
1102 struct lens *lens = xfm_lens(aug, xfm, &lens_name);
1109 err_status = "lens_name";
1113 copy_if_rename_fails =
1114 aug_get(aug, AUGEAS_COPY_IF_RENAME_FAILS, NULL) == 1;
1116 if (asprintf(&augorig, "%s%s", aug->root, filename) == -1) {
1121 augorig_canon = canonicalize_file_name(augorig);
1123 if (augorig_canon == NULL) {
1124 if (errno == ENOENT) {
1125 augorig_canon = augorig;
1128 err_status = "canon_augorig";
1133 if (access(augorig_canon, R_OK) == 0) {
1134 augorig_canon_fp = fopen(augorig_canon, "r");
1135 text = xfread_file(augorig_canon_fp);
1141 err_status = "put_read";
1145 text = append_newline(text, strlen(text));
1147 /* Figure out where to put the .augnew and temp file. If no .augnew file
1148 then put the temp file next to augorig_canon, else next to .augnew. */
1149 if (aug->flags & AUG_SAVE_NEWFILE) {
1150 if (xasprintf(&augnew, "%s" EXT_AUGNEW, augorig) < 0) {
1151 err_status = "augnew_oom";
1156 augdest = augorig_canon;
1159 if (xasprintf(&augtemp, "%s.XXXXXX", augdest) < 0) {
1160 err_status = "augtemp_oom";
1164 // FIXME: We might have to create intermediate directories
1165 // to be able to write augnew, but we have no idea what permissions
1166 // etc. they should get. Just the process default ?
1167 fd = mkstemp(augtemp);
1169 err_status = "mk_augtemp";
1172 fp = fdopen(fd, "w");
1174 err_status = "open_augtemp";
1178 if (augorig_exists) {
1179 if (transfer_file_attrs(augorig_canon_fp, fp, &err_status) != 0) {
1183 /* Since mkstemp is used, the temp file will have secure permissions
1184 * instead of those implied by umask, so change them for new files */
1185 mode_t curumsk = umask(022);
1188 if (fchmod(fileno(fp), 0666 & ~curumsk) < 0) {
1189 err_status = "create_chmod";
1195 lns_put(fp, lens, tree->children, text, &err);
1198 err_status = "error_augtemp";
1202 if (fflush(fp) != 0) {
1203 err_status = "flush_augtemp";
1207 if (fsync(fileno(fp)) < 0) {
1208 err_status = "sync_augtemp";
1212 if (fclose(fp) != 0) {
1213 err_status = "close_augtemp";
1221 err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
1227 char *new_text = xread_file(augtemp);
1229 if (new_text == NULL) {
1230 err_status = "read_augtemp";
1233 same = STREQ(text, new_text);
1239 } else if (aug->flags & AUG_SAVE_NOOP) {
1246 if (!(aug->flags & AUG_SAVE_NEWFILE)) {
1247 if (augorig_exists && (aug->flags & AUG_SAVE_BACKUP)) {
1248 r = xasprintf(&augsave, "%s" EXT_AUGSAVE, augorig);
1254 r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
1256 dyn_err_status = strappend(err_status, "_augsave");
1262 r = clone_file(augtemp, augdest, &err_status, copy_if_rename_fails, 0);
1264 dyn_err_status = strappend(err_status, "_augtemp");
1271 force_reload = aug->flags & AUG_SAVE_NEWFILE;
1272 r = add_file_info(aug, path, lens, lens_name, augorig, force_reload);
1274 err_status = "file_info";
1278 r = file_saved_event(aug, path);
1280 err_status = "saved_event";
1286 dyn_err_status == NULL ? err_status : dyn_err_status;
1287 store_error(aug, filename, path, emsg, errno, err, text);
1289 free(dyn_err_status);
1294 if (augorig_canon != augorig)
1295 free(augorig_canon);
1298 free_lns_error(err);
1302 if (augorig_canon_fp != NULL)
1303 fclose(augorig_canon_fp);
1307 int text_retrieve(struct augeas *aug, const char *lens_name,
1308 const char *path, struct tree *tree,
1309 const char *text_in, char **text_out) {
1310 struct memstream ms;
1311 bool ms_open = false;
1312 const char *err_status = NULL;
1313 struct lns_error *err = NULL;
1314 struct lens *lens = NULL;
1320 lens = lens_from_name(aug, lens_name);
1322 err_status = "lens_name";
1326 r = init_memstream(&ms);
1328 err_status = "init_memstream";
1334 lns_put(ms.stream, lens, tree->children, text_in, &err);
1336 r = close_memstream(&ms);
1339 err_status = "close_memstream";
1347 err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
1354 store_error(aug, NULL, path, err_status, errno, err, text_in);
1360 free_lns_error(err);
1363 close_memstream(&ms);
1367 int remove_file(struct augeas *aug, struct tree *tree) {
1368 const char *err_status = NULL;
1369 char *dyn_err_status = NULL;
1370 char *augsave = NULL, *augorig = NULL, *augorig_canon = NULL;
1371 struct tree *path = NULL;
1372 const char *file_path = NULL;
1373 char *meta_path = NULL;
1376 path = tree_child(tree, s_path);
1378 err_status = "no child called 'path' for file entry";
1381 file_path = path->value + strlen(AUGEAS_FILES_TREE);
1384 meta_path = path_of_tree(tree);
1385 if (meta_path == NULL) {
1386 err_status = "path_of_tree";
1390 if ((augorig = strappend(aug->root, file_path)) == NULL) {
1391 err_status = "root_file";
1395 augorig_canon = canonicalize_file_name(augorig);
1396 if (augorig_canon == NULL) {
1397 if (errno == ENOENT) {
1400 err_status = "canon_augorig";
1405 r = file_saved_event(aug, meta_path + strlen(AUGEAS_META_TREE));
1407 err_status = "saved_event";
1411 if (aug->flags & AUG_SAVE_NOOP)
1414 if (aug->flags & AUG_SAVE_BACKUP) {
1415 /* Move file to one with extension .augsave */
1416 r = asprintf(&augsave, "%s" EXT_AUGSAVE, augorig_canon);
1422 r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
1424 dyn_err_status = strappend(err_status, "_augsave");
1429 r = unlink(augorig_canon);
1431 err_status = "unlink_orig";
1436 tree_unlink(aug, tree);
1440 free(augorig_canon);
1446 dyn_err_status == NULL ? err_status : dyn_err_status;
1447 store_error(aug, file_path, meta_path, emsg, errno, NULL, NULL);
1451 free(augorig_canon);
1453 free(dyn_err_status);
1459 * indent-tabs-mode: nil