Imported Upstream version 1.8.0
[platform/upstream/augeas.git] / src / transform.c
1 /*
2  * transform.c: support for building and running transformers
3  *
4  * Copyright (C) 2007-2016 David Lutterkort
5  *
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.
10  *
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.
15  *
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
19  *
20  * Author: David Lutterkort <dlutter@redhat.com>
21  */
22
23 #include <config.h>
24
25 #include <fnmatch.h>
26 #include <glob.h>
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <selinux/selinux.h>
33 #include <stdbool.h>
34
35 #include "internal.h"
36 #include "memory.h"
37 #include "augeas.h"
38 #include "syntax.h"
39 #include "transform.h"
40 #include "errcode.h"
41
42 static const int fnm_flags = FNM_PATHNAME;
43 static const int glob_flags = GLOB_NOSORT;
44
45 /* Extension for newly created files */
46 #define EXT_AUGNEW ".augnew"
47 /* Extension for backup files */
48 #define EXT_AUGSAVE ".augsave"
49
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
61  */
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";
68
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";
75
76 /*
77  * Filters
78  */
79 struct filter *make_filter(struct string *glb, unsigned int include) {
80     struct filter *f;
81     make_ref(f);
82     f->glob = glb;
83     f->include = include;
84     return f;
85 }
86
87 void free_filter(struct filter *f) {
88     if (f == NULL)
89         return;
90     assert(f->ref == 0);
91     unref(f->next, filter);
92     unref(f->glob, string);
93     free(f);
94 }
95
96 static const char *pathbase(const char *path) {
97     const char *p = strrchr(path, SEP);
98     return (p == NULL) ? path : p + 1;
99 }
100
101 static bool is_excl(struct tree *f) {
102     return streqv(f->label, "excl") && f->value != NULL;
103 }
104
105 static bool is_incl(struct tree *f) {
106     return streqv(f->label, "incl") && f->value != NULL;
107 }
108
109 static bool is_regular_file(const char *path) {
110     int r;
111     struct stat st;
112
113     r = stat(path, &st);
114     if (r < 0)
115         return false;
116     return S_ISREG(st.st_mode);
117 }
118
119 static char *mtime_as_string(struct augeas *aug, const char *fname) {
120     int r;
121     struct stat st;
122     char *result = NULL;
123
124     if (fname == NULL) {
125         result = strdup("0");
126         ERR_NOMEM(result == NULL, aug);
127         goto done;
128     }
129
130     r = stat(fname, &st);
131     if (r < 0) {
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);
136     } else {
137         r = xasprintf(&result, "%ld", (long) st.st_mtime);
138         ERR_NOMEM(r < 0, aug);
139     }
140  done:
141     return result;
142  error:
143     FREE(result);
144     return NULL;
145 }
146
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) {
149     int i, j, r;
150     char *pattern_norm = NULL;
151
152     r = ALLOC_N(pattern_norm, strlen(pattern) + 1);
153     if (r < 0)
154         goto error;
155
156     for (i = 0, j = 0; i < strlen(pattern); i++) {
157         if (pattern[i] != '/' || pattern[i+1] != '/') {
158             pattern_norm[j] = pattern[i];
159             j++;
160         }
161     }
162     pattern_norm[j] = 0;
163
164     r = fnmatch(pattern_norm, string, flags);
165     FREE(pattern_norm);
166     return r;
167
168  error:
169     if (pattern_norm != NULL)
170         FREE(pattern_norm);
171     return -1;
172 }
173
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;
178     int r;
179     struct stat st;
180     int64_t mtime_i;
181
182     if (mtime == NULL || mtime->value == NULL)
183         return false;
184
185     r = xstrtoint64(mtime->value, 10, &mtime_i);
186     if (r < 0) {
187         /* Ignore silently and err on the side of caution */
188         return false;
189     }
190
191     r = stat(fname, &st);
192     if (r < 0)
193         return false;
194
195     if (mtime_i != (int64_t) st.st_mtime)
196         return false;
197
198     path = tree_child(finfo, s_path);
199     if (path == NULL)
200         return false;
201
202     file = tree_fpath(aug, path->value);
203     return (file != NULL && ! file->dirty);
204 }
205
206 static int filter_generate(struct tree *xfm, const char *root,
207                            int *nmatches, char ***matches) {
208     glob_t globbuf;
209     int gl_flags = glob_flags;
210     int r;
211     int ret = 0;
212     char **pathv = NULL;
213     int pathc = 0;
214     int root_prefix = strlen(root) - 1;
215
216     *nmatches = 0;
217     *matches = NULL;
218     MEMZERO(&globbuf, 1);
219
220     list_for_each(f, xfm->children) {
221         char *globpat = NULL;
222         if (! is_incl(f))
223             continue;
224         pathjoin(&globpat, 2, root, f->value);
225         r = glob(globpat, gl_flags, NULL, &globbuf);
226         free(globpat);
227
228         if (r != 0 && r != GLOB_NOMATCH)
229             goto error;
230         gl_flags |= GLOB_APPEND;
231     }
232
233     pathc = globbuf.gl_pathc;
234     int pathind = 0;
235
236     if (ALLOC_N(pathv, pathc) < 0)
237         goto error;
238
239     for (int i=0; i < pathc; i++) {
240         const char *path = globbuf.gl_pathv[i] + root_prefix;
241         bool include = true;
242
243         list_for_each(e, xfm->children) {
244             if (! is_excl(e))
245                 continue;
246
247             if (strchr(e->value, SEP) == NULL)
248                 path = pathbase(path);
249
250             r = fnmatch_normalize(e->value, path, fnm_flags);
251             if (r < 0)
252                 goto error;
253             else if (r == 0)
254                 include = false;
255         }
256
257         if (include)
258             include = is_regular_file(globbuf.gl_pathv[i]);
259
260         if (include) {
261             pathv[pathind] = strdup(globbuf.gl_pathv[i]);
262             if (pathv[pathind] == NULL)
263                 goto error;
264             pathind += 1;
265         }
266     }
267     pathc = pathind;
268
269     if (REALLOC_N(pathv, pathc) == -1)
270         goto error;
271
272     *matches = pathv;
273     *nmatches = pathc;
274  done:
275     globfree(&globbuf);
276     return ret;
277  error:
278     if (pathv != NULL)
279         for (int i=0; i < pathc; i++)
280             free(pathv[i]);
281     free(pathv);
282     ret = -1;
283     goto done;
284 }
285
286 int filter_matches(struct tree *xfm, const char *path) {
287     int found = 0;
288     list_for_each(f, xfm->children) {
289         if (is_incl(f) && fnmatch_normalize(f->value, path, fnm_flags) == 0) {
290             found = 1;
291             break;
292         }
293     }
294     if (! found)
295         return 0;
296     list_for_each(f, xfm->children) {
297         if (is_excl(f) && (fnmatch_normalize(f->value, path, fnm_flags) == 0))
298             return 0;
299     }
300     return 1;
301 }
302
303 /*
304  * Transformers
305  */
306 struct transform *make_transform(struct lens *lens, struct filter *filter) {
307     struct transform *xform;
308     make_ref(xform);
309     xform->lens = lens;
310     xform->filter = filter;
311     return xform;
312 }
313
314 void free_transform(struct transform *xform) {
315     if (xform == NULL)
316         return;
317     assert(xform->ref == 0);
318     unref(xform->lens, lens);
319     unref(xform->filter, filter);
320     free(xform);
321 }
322
323 static char *err_path(const char *filename) {
324     char *result = NULL;
325     if (filename == NULL)
326         pathjoin(&result, 2, AUGEAS_META_FILES, s_error);
327     else
328         pathjoin(&result, 3, AUGEAS_META_FILES, filename, s_error);
329     return result;
330 }
331
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, ...) {
336     int r;
337     va_list ap;
338     char *value = NULL;
339     struct tree *tree = NULL;
340
341     va_start(ap, format);
342     r = vasprintf(&value, format, ap);
343     va_end(ap);
344     if (r < 0)
345         value = NULL;
346     ERR_NOMEM(r < 0, aug);
347
348     tree = tree_child_cr(err_info, sub);
349     ERR_NOMEM(tree == NULL, aug);
350
351     r = tree_set_value(tree, value);
352     ERR_NOMEM(r < 0, aug);
353
354  error:
355     free(value);
356     return tree;
357 }
358
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;
362
363     if (lens == NULL)
364         return NULL;
365
366     char *fi = format_info(lens->info);
367     if (fi != NULL) {
368         result = err_set(aug, where, label, "%s", fi);
369         free(fi);
370     }
371     return result;
372 }
373
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.
379  */
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;
385     char *fip = NULL;
386     int r;
387     int result = -1;
388
389     if (filename != NULL) {
390         r = pathjoin(&fip, 2, AUGEAS_META_FILES, filename);
391     } else {
392         r = pathjoin(&fip, 2, AUGEAS_META_TEXT, path);
393     }
394     ERR_NOMEM(r < 0, aug);
395
396     finfo = tree_fpath_cr(aug, fip);
397     ERR_BAIL(aug);
398
399     if (status != NULL) {
400         err_info = tree_child_cr(finfo, s_error);
401         ERR_NOMEM(err_info == NULL, aug);
402
403         r = tree_set_value(err_info, status);
404         ERR_NOMEM(r < 0, aug);
405
406         /* Errors from err_set are ignored on purpose. We try
407          * to report as much as we can */
408         if (err != NULL) {
409             if (err->pos >= 0) {
410                 size_t line, ofs;
411                 err_set(aug, err_info, s_pos, "%d", err->pos);
412                 if (text != NULL) {
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);
416                 }
417             }
418             if (err->path != NULL) {
419                 err_set(aug, err_info, s_path, "%s%s", path, err->path);
420             }
421             struct tree *t = err_lens_entry(aug, err_info, err->lens, s_lens);
422             if (t != NULL) {
423                 err_lens_entry(aug, t, err->last, s_last);
424                 err_lens_entry(aug, t, err->next, s_next);
425             }
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);
430         }
431     } else {
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);
436     }
437
438     tree_clean(finfo);
439     result = 0;
440  error:
441     free(fip);
442     return result;
443 }
444
445 /* Set up the file information in the /augeas tree.
446  *
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.
450  *
451  * Returns 0 on success, -1 on error
452  */
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;
457     char *tmp = NULL;
458     int r;
459     char *path = NULL;
460     int result = -1;
461
462     if (lens == NULL)
463         return -1;
464
465     r = pathjoin(&path, 2, AUGEAS_META_TREE, node);
466     ERR_NOMEM(r < 0, aug);
467
468     file = tree_fpath_cr(aug, path);
469     file->file = true;
470     ERR_BAIL(aug);
471
472     /* Set '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);
477
478     /* Set 'mtime' */
479     if (force_reload) {
480         tmp = strdup("0");
481         ERR_NOMEM(tmp == NULL, aug);
482     } else {
483         tmp = mtime_as_string(aug, filename);
484         ERR_BAIL(aug);
485     }
486     tree = tree_child_cr(file, s_mtime);
487     ERR_NOMEM(tree == NULL, aug);
488     tree_store_value(tree, &tmp);
489
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);
497     FREE(tmp);
498
499     /* Set 'lens' */
500     tree = tree->parent;
501     r = tree_set_value(tree, lens_name);
502     ERR_NOMEM(r < 0, aug);
503
504     tree_clean(file);
505
506     result = 0;
507  error:
508     free(path);
509     free(tmp);
510     return result;
511 }
512
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) {
519             text[len] = '\n';
520             text[len+1] = '\0';
521         }
522     }
523     return text;
524 }
525
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) {
529     char *path = NULL;
530
531     pathjoin(&path, 2, AUGEAS_FILES_TREE, fname + strlen(aug->root) - 1);
532     return path;
533 }
534
535 /* Replace the subtree for FPATH with SUB */
536 static void tree_freplace(struct augeas *aug, const char *fpath,
537                          struct tree *sub) {
538     struct tree *parent;
539
540     parent = tree_fpath_cr(aug, fpath);
541     ERR_RET(aug);
542
543     parent->file = true;
544     tree_unlink_children(aug, parent);
545     list_append(parent->children, sub);
546     list_for_each(s, sub) {
547         s->parent = parent;
548     }
549 }
550
551 static int load_file(struct augeas *aug, struct lens *lens,
552                      const char *lens_name, char *filename) {
553     char *text = NULL;
554     const char *err_status = NULL;
555     struct tree *tree = NULL;
556     char *path = 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;
561
562     path = file_name_path(aug, filename);
563     ERR_NOMEM(path == NULL, aug);
564
565     r = add_file_info(aug, path, lens, lens_name, filename, false);
566     if (r < 0)
567         goto done;
568
569     text = xread_file(filename);
570     if (text == NULL) {
571         err_status = "read_failed";
572         goto done;
573     }
574     text_len = strlen(text);
575     text = append_newline(text, text_len);
576
577     make_ref(info);
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;
583
584     if (aug->flags & AUG_ENABLE_SPAN) {
585         /* Allocate the span already to capture a reference to
586            info->filename */
587         span = make_span(info);
588         ERR_NOMEM(span == NULL, info);
589     }
590
591     tree = lns_get(info, lens, text, &err);
592
593     if (err != NULL) {
594         err_status = "parse_failed";
595         goto done;
596     }
597
598     tree_freplace(aug, path, tree);
599     ERR_BAIL(aug);
600
601     /* top level node span entire file length */
602     if (span != NULL && tree != NULL) {
603         tree->parent->span = span;
604         span = NULL;
605         tree->parent->span->span_start = 0;
606         tree->parent->span->span_end = text_len;
607     }
608
609     tree = NULL;
610
611     result = 0;
612  done:
613     store_error(aug, filename + strlen(aug->root) - 1, path, err_status,
614                 errno, err, text);
615  error:
616     unref(info, info);
617     free_lns_error(err);
618     free(path);
619     free_span(span);
620     free_tree(tree);
621     free(text);
622     return result;
623 }
624
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
629  */
630 static struct lens *lens_from_name(struct augeas *aug, const char *name) {
631     struct lens *result = NULL;
632
633     if (name[0] == '@') {
634         struct module *modl = NULL;
635         for (modl = aug->modules;
636              modl != NULL && !streqv(modl->name, name + 1);
637              modl = modl->next);
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;
643     } else {
644         result = lens_lookup(aug, name);
645     }
646     ERR_THROW(result == NULL, aug, AUG_ENOLENS,
647               "Can not find lens %s", name);
648     return result;
649  error:
650     return NULL;
651 }
652
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;
658     int result = -1;
659     const char *err_status = NULL;
660     struct lens *lens = NULL;
661
662     lens = lens_from_name(aug, lens_path);
663     ERR_BAIL(aug);
664
665     make_ref(info);
666     info->first_line = 1;
667     info->last_line = 1;
668     info->first_column = 1;
669     info->last_column = strlen(text);
670
671     tree = lns_get(info, lens, text, &err);
672     if (err != NULL) {
673         err_status = "parse_failed";
674         goto error;
675     }
676
677     tree_freplace(aug, path, tree);
678     ERR_BAIL(aug);
679
680     tree = NULL;
681
682     result = 0;
683  error:
684     unref(info, info);
685     store_error(aug, NULL, path, err_status, errno, err, text);
686     free_tree(tree);
687     free_lns_error(err);
688     return result;
689 }
690
691 const char *xfm_lens_name(struct tree *xfm) {
692     struct tree *l = tree_child(xfm, s_lens);
693
694     if (l == NULL)
695         return "(unknown)";
696     if (l->value == NULL)
697         return "(noname)";
698     return l->value;
699 }
700
701 struct lens *xfm_lens(struct augeas *aug,
702                       struct tree *xfm, const char **lens_name) {
703     struct tree *l = NULL;
704
705     for (l = xfm->children;
706          l != NULL && !streqv("lens", l->label);
707          l = l->next);
708
709     if (l == NULL || l->value == NULL)
710         return NULL;
711     if (lens_name != NULL)
712         *lens_name = l->value;
713
714     return lens_from_name(aug, l->value);
715 }
716
717 static void xfm_error(struct tree *xfm, const char *msg) {
718     char *v = msg ? strdup(msg) : NULL;
719     char *l = strdup("error");
720
721     if (l == NULL || v == NULL) {
722         free(v);
723         free(l);
724         return;
725     }
726     tree_append(xfm, l, v);
727 }
728
729 int transform_validate(struct augeas *aug, struct tree *xfm) {
730     struct tree *l = NULL;
731
732     for (struct tree *t = xfm->children; t != NULL; ) {
733         if (streqv(t->label, "lens")) {
734             l = t;
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 */
738             int r;
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);
742             t->value[0] = SEP;
743         }
744
745         if (streqv(t->label, "error")) {
746             struct tree *del = t;
747             t = del->next;
748             tree_unlink(aug, del);
749         } else {
750             t = t->next;
751         }
752     }
753
754     if (l == NULL) {
755         xfm_error(xfm, "missing a child with label 'lens'");
756         return -1;
757     }
758     if (l->value == NULL) {
759         xfm_error(xfm, "the 'lens' node does not contain a lens name");
760         return -1;
761     }
762     lens_from_name(aug, l->value);
763     ERR_BAIL(aug);
764
765     return 0;
766  error:
767     xfm_error(xfm, aug->error->details);
768     return -1;
769 }
770
771 void transform_file_error(struct augeas *aug, const char *status,
772                           const char *filename, const char *format, ...) {
773     char *ep = err_path(filename);
774     struct tree *err;
775     char *msg;
776     va_list ap;
777     int r;
778
779     err = tree_fpath_cr(aug, ep);
780     FREE(ep);
781     if (err == NULL)
782         return;
783
784     tree_unlink_children(aug, err);
785     tree_set_value(err, status);
786
787     err = tree_child_cr(err, s_message);
788     if (err == NULL)
789         return;
790
791     va_start(ap, format);
792     r = vasprintf(&msg, format, ap);
793     va_end(ap);
794     if (r < 0)
795         return;
796     tree_set_value(err, msg);
797     free(msg);
798 }
799
800 static struct tree *file_info(struct augeas *aug, const char *fname) {
801     char *path = NULL;
802     struct tree *result = NULL;
803     int r;
804
805     r = pathjoin(&path, 2, AUGEAS_META_FILES, fname);
806     ERR_NOMEM(r < 0, aug);
807
808     result = tree_fpath(aug, path);
809     ERR_BAIL(aug);
810  error:
811     free(path);
812     return result;
813 }
814
815 int transform_load(struct augeas *aug, struct tree *xfm, const char *file) {
816     int nmatches = 0;
817     char **matches;
818     const char *lens_name;
819     struct lens *lens = xfm_lens(aug, xfm, &lens_name);
820     int r;
821
822     if (lens == NULL) {
823         // FIXME: Record an error and return 0
824         return -1;
825     }
826
827     r = filter_generate(xfm, aug->root, &nmatches, &matches);
828     if (r == -1)
829         return -1;
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);
833
834         if (file != NULL && STRNEQ(filename, file)) {
835             FREE(matches[i]);
836             continue;
837         }
838
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
849                did. */
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",
856                                      s, lens_name);
857                 aug_rm(aug, fpath);
858                 free(fpath);
859             }
860         } else if (!file_current(aug, matches[i], finfo)) {
861             load_file(aug, lens, lens_name, matches[i]);
862         }
863         if (finfo != NULL)
864             finfo->dirty = 0;
865         FREE(matches[i]);
866     }
867     lens_release(lens);
868     free(matches);
869     return 0;
870 }
871
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)
875         return 0;
876     return filter_matches(xfm, path + strlen(AUGEAS_FILES_TREE));
877 }
878
879 static int transfer_file_attrs(FILE *from, FILE *to,
880                                const char **err_status) {
881     struct stat st;
882     int ret = 0;
883     int selinux_enabled = (is_selinux_enabled() > 0);
884     security_context_t con = NULL;
885
886     int from_fd;
887     int to_fd = fileno(to);
888
889     if (from == NULL) {
890         *err_status = "replace_from_missing";
891         return -1;
892     }
893
894     from_fd = fileno(from);
895
896     ret = fstat(from_fd, &st);
897     if (ret < 0) {
898         *err_status = "replace_stat";
899         return -1;
900     }
901     if (selinux_enabled) {
902         if (fgetfilecon(from_fd, &con) < 0 && errno != ENOTSUP) {
903             *err_status = "replace_getfilecon";
904             return -1;
905         }
906     }
907
908     if (fchown(to_fd, st.st_uid, st.st_gid) < 0) {
909         *err_status = "replace_chown";
910         return -1;
911     }
912     if (fchmod(to_fd, st.st_mode) < 0) {
913         *err_status = "replace_chmod";
914         return -1;
915     }
916     if (selinux_enabled && con != NULL) {
917         if (fsetfilecon(to_fd, con) < 0 && errno != ENOTSUP) {
918             *err_status = "replace_setfilecon";
919             return -1;
920         }
921         freecon(con);
922     }
923     return 0;
924 }
925
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.
930  *
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).
935  *
936  * Return 0 on success (either rename succeeded or we copied the contents
937  * over successfully), -1 on failure.
938  */
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;
943     char buf[BUFSIZ];
944     size_t len;
945     int to_fd = -1, to_oflags, r;
946     int result = -1;
947
948     if (rename(from, to) == 0)
949         return 0;
950     if ((errno != EXDEV && errno != EBUSY) || !copy_if_rename_fails) {
951         *err_status = "rename";
952         return -1;
953     }
954
955     /* rename not possible, copy file contents */
956     if (!(from_fp = fopen(from, "r"))) {
957         *err_status = "clone_open_src";
958         goto done;
959     }
960
961     if (unlink_if_rename_fails) {
962         r = unlink(to);
963         if (r < 0) {
964             *err_status = "clone_unlink_dst";
965             goto done;
966         }
967     }
968
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";
972         goto done;
973     }
974     if (!(to_fp = fdopen(to_fd, "w"))) {
975         *err_status = "clone_fdopen_dst";
976         goto done;
977     }
978
979     if (transfer_file_attrs(from_fp, to_fp, err_status) < 0)
980         goto done;
981
982     while ((len = fread(buf, 1, BUFSIZ, from_fp)) > 0) {
983         if (fwrite(buf, 1, len, to_fp) != len) {
984             *err_status = "clone_write";
985             goto done;
986         }
987     }
988     if (ferror(from_fp)) {
989         *err_status = "clone_read";
990         goto done;
991     }
992     if (fflush(to_fp) != 0) {
993         *err_status = "clone_flush";
994         goto done;
995     }
996     if (fsync(fileno(to_fp)) < 0) {
997         *err_status = "clone_sync";
998         goto done;
999     }
1000     result = 0;
1001  done:
1002     if (from_fp != NULL)
1003         fclose(from_fp);
1004     if (to_fp != NULL) {
1005         if (fclose(to_fp) != 0) {
1006             *err_status = "clone_fclose_dst";
1007             result = -1;
1008         }
1009     } else if (to_fd >= 0 && close(to_fd) < 0) {
1010         *err_status = "clone_close_dst";
1011         result = -1;
1012     }
1013     if (result != 0)
1014         unlink(to);
1015     if (result == 0)
1016         unlink(from);
1017     return result;
1018 }
1019
1020 static char *strappend(const char *s1, const char *s2) {
1021     size_t len = strlen(s1) + strlen(s2);
1022     char *result = NULL, *p;
1023
1024     if (ALLOC_N(result, len + 1) < 0)
1025         return NULL;
1026
1027     p = stpcpy(result, s1);
1028     stpcpy(p, s2);
1029     return result;
1030 }
1031
1032 static int file_saved_event(struct augeas *aug, const char *path) {
1033     const char *saved = strrchr(AUGEAS_EVENTS_SAVED, SEP) + 1;
1034     struct pathx *px;
1035     struct tree *dummy;
1036     int r;
1037
1038     px = pathx_aug_parse(aug, aug->origin, NULL,
1039                          AUGEAS_EVENTS_SAVED "[last()]", true);
1040     ERR_BAIL(aug);
1041
1042     if (pathx_find_one(px, &dummy) == 1) {
1043         r = tree_insert(px, saved, 0);
1044         if (r < 0)
1045             goto error;
1046     }
1047
1048     if (! tree_set(px, path))
1049         goto error;
1050
1051     free_pathx(px);
1052     return 0;
1053  error:
1054     free_pathx(px);
1055     return -1;
1056 }
1057
1058 /*
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
1061  * PATH/error.
1062  *
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
1065  * back to PATH.
1066  *
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.
1069  *
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.)
1073  *
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.
1076  *
1077  * The table below shows the locations for each permutation.
1078  *
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    -
1086  *
1087  * Return 0 on success, -1 on failure.
1088  */
1089 int transform_save(struct augeas *aug, struct tree *xfm,
1090                    const char *path, struct tree *tree) {
1091     int   fd;
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;
1095     int   augorig_exists;
1096     int   copy_if_rename_fails = 0;
1097     char *text = NULL;
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);
1104     int result = -1, r;
1105     bool force_reload;
1106
1107     errno = 0;
1108
1109     if (lens == NULL) {
1110         err_status = "lens_name";
1111         goto done;
1112     }
1113
1114     copy_if_rename_fails =
1115         aug_get(aug, AUGEAS_COPY_IF_RENAME_FAILS, NULL) == 1;
1116
1117     if (asprintf(&augorig, "%s%s", aug->root, filename) == -1) {
1118         augorig = NULL;
1119         goto done;
1120     }
1121
1122     augorig_canon = canonicalize_file_name(augorig);
1123     augorig_exists = 1;
1124     if (augorig_canon == NULL) {
1125         if (errno == ENOENT) {
1126             augorig_canon = augorig;
1127             augorig_exists = 0;
1128         } else {
1129             err_status = "canon_augorig";
1130             goto done;
1131         }
1132     }
1133
1134     if (access(augorig_canon, R_OK) == 0) {
1135         augorig_canon_fp = fopen(augorig_canon, "r");
1136         text = xfread_file(augorig_canon_fp);
1137     } else {
1138         text = strdup("");
1139     }
1140
1141     if (text == NULL) {
1142         err_status = "put_read";
1143         goto done;
1144     }
1145
1146     text = append_newline(text, strlen(text));
1147
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";
1153             goto done;
1154         }
1155         augdest = augnew;
1156     } else {
1157         augdest = augorig_canon;
1158     }
1159
1160     if (xasprintf(&augtemp, "%s.XXXXXX", augdest) < 0) {
1161         err_status = "augtemp_oom";
1162         goto done;
1163     }
1164
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);
1169     if (fd < 0) {
1170         err_status = "mk_augtemp";
1171         goto done;
1172     }
1173     fp = fdopen(fd, "w");
1174     if (fp == NULL) {
1175         err_status = "open_augtemp";
1176         goto done;
1177     }
1178
1179     if (augorig_exists) {
1180         if (transfer_file_attrs(augorig_canon_fp, fp, &err_status) != 0) {
1181             goto done;
1182         }
1183     } else {
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);
1187         umask(curumsk);
1188
1189         if (fchmod(fileno(fp), 0666 & ~curumsk) < 0) {
1190             err_status = "create_chmod";
1191             return -1;
1192         }
1193     }
1194
1195     if (tree != NULL)
1196         lns_put(fp, lens, tree->children, text, &err);
1197
1198     if (ferror(fp)) {
1199         err_status = "error_augtemp";
1200         goto done;
1201     }
1202
1203     if (fflush(fp) != 0) {
1204         err_status = "flush_augtemp";
1205         goto done;
1206     }
1207
1208     if (fsync(fileno(fp)) < 0) {
1209         err_status = "sync_augtemp";
1210         goto done;
1211     }
1212
1213     if (fclose(fp) != 0) {
1214         err_status = "close_augtemp";
1215         fp = NULL;
1216         goto done;
1217     }
1218
1219     fp = NULL;
1220
1221     if (err != NULL) {
1222         err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
1223         unlink(augtemp);
1224         goto done;
1225     }
1226
1227     {
1228         char *new_text = xread_file(augtemp);
1229         int same = 0;
1230         if (new_text == NULL) {
1231             err_status = "read_augtemp";
1232             goto done;
1233         }
1234         same = STREQ(text, new_text);
1235         FREE(new_text);
1236         if (same) {
1237             result = 0;
1238             unlink(augtemp);
1239             goto done;
1240         } else if (aug->flags & AUG_SAVE_NOOP) {
1241             result = 1;
1242             unlink(augtemp);
1243             goto done;
1244         }
1245     }
1246
1247     if (!(aug->flags & AUG_SAVE_NEWFILE)) {
1248         if (augorig_exists && (aug->flags & AUG_SAVE_BACKUP)) {
1249             r = xasprintf(&augsave, "%s" EXT_AUGSAVE, augorig);
1250             if (r == -1) {
1251                 augsave = NULL;
1252                 goto done;
1253             }
1254
1255             r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
1256             if (r != 0) {
1257                 dyn_err_status = strappend(err_status, "_augsave");
1258                 goto done;
1259             }
1260         }
1261     }
1262
1263     r = clone_file(augtemp, augdest, &err_status, copy_if_rename_fails, 0);
1264     if (r != 0) {
1265         dyn_err_status = strappend(err_status, "_augtemp");
1266         goto done;
1267     }
1268
1269     result = 1;
1270
1271  done:
1272     force_reload = aug->flags & AUG_SAVE_NEWFILE;
1273     r = add_file_info(aug, path, lens, lens_name, augorig, force_reload);
1274     if (r < 0) {
1275         err_status = "file_info";
1276         result = -1;
1277     }
1278     if (result > 0) {
1279         r = file_saved_event(aug, path);
1280         if (r < 0) {
1281             err_status = "saved_event";
1282             result = -1;
1283         }
1284     }
1285     {
1286         const char *emsg =
1287             dyn_err_status == NULL ? err_status : dyn_err_status;
1288         store_error(aug, filename, path, emsg, errno, err, text);
1289     }
1290     free(dyn_err_status);
1291     lens_release(lens);
1292     free(text);
1293     free(augtemp);
1294     free(augnew);
1295     if (augorig_canon != augorig)
1296         free(augorig_canon);
1297     free(augorig);
1298     free(augsave);
1299     free_lns_error(err);
1300
1301     if (fp != NULL)
1302         fclose(fp);
1303     if (augorig_canon_fp != NULL)
1304         fclose(augorig_canon_fp);
1305     return result;
1306 }
1307
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;
1316     int result = -1, r;
1317
1318     MEMZERO(&ms, 1);
1319     errno = 0;
1320
1321     lens = lens_from_name(aug, lens_name);
1322     if (lens == NULL) {
1323         err_status = "lens_name";
1324         goto done;
1325     }
1326
1327     r = init_memstream(&ms);
1328     if (r < 0) {
1329         err_status = "init_memstream";
1330         goto done;
1331     }
1332     ms_open = true;
1333
1334     if (tree != NULL)
1335         lns_put(ms.stream, lens, tree->children, text_in, &err);
1336
1337     r = close_memstream(&ms);
1338     ms_open = false;
1339     if (r < 0) {
1340         err_status = "close_memstream";
1341         goto done;
1342     }
1343
1344     *text_out = ms.buf;
1345     ms.buf = NULL;
1346
1347     if (err != NULL) {
1348         err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
1349         goto done;
1350     }
1351
1352     result = 0;
1353
1354  done:
1355     store_error(aug, NULL, path, err_status, errno, err, text_in);
1356     lens_release(lens);
1357     if (result < 0) {
1358         free(*text_out);
1359         *text_out = NULL;
1360     }
1361     free_lns_error(err);
1362
1363     if (ms_open)
1364         close_memstream(&ms);
1365     return result;
1366 }
1367
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;
1375     int r;
1376
1377     path = tree_child(tree, s_path);
1378     if (path == NULL) {
1379         err_status = "no child called 'path' for file entry";
1380         goto error;
1381     }
1382     file_path = path->value + strlen(AUGEAS_FILES_TREE);
1383     path = NULL;
1384
1385     meta_path = path_of_tree(tree);
1386     if (meta_path == NULL) {
1387         err_status = "path_of_tree";
1388         goto error;
1389     }
1390
1391     if ((augorig = strappend(aug->root, file_path)) == NULL) {
1392         err_status = "root_file";
1393         goto error;
1394     }
1395
1396     augorig_canon = canonicalize_file_name(augorig);
1397     if (augorig_canon == NULL) {
1398         if (errno == ENOENT) {
1399             goto done;
1400         } else {
1401             err_status = "canon_augorig";
1402             goto error;
1403         }
1404     }
1405
1406     r = file_saved_event(aug, meta_path + strlen(AUGEAS_META_TREE));
1407     if (r < 0) {
1408         err_status = "saved_event";
1409         goto error;
1410     }
1411
1412     if (aug->flags & AUG_SAVE_NOOP)
1413         goto done;
1414
1415     if (aug->flags & AUG_SAVE_BACKUP) {
1416         /* Move file to one with extension .augsave */
1417         r = asprintf(&augsave, "%s" EXT_AUGSAVE, augorig_canon);
1418         if (r == -1) {
1419             augsave = NULL;
1420                 goto error;
1421         }
1422
1423         r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
1424         if (r != 0) {
1425             dyn_err_status = strappend(err_status, "_augsave");
1426             goto error;
1427         }
1428     } else {
1429         /* Unlink file */
1430         r = unlink(augorig_canon);
1431         if (r < 0) {
1432             err_status = "unlink_orig";
1433             goto error;
1434         }
1435     }
1436     path = NULL;
1437     tree_unlink(aug, tree);
1438  done:
1439     free(meta_path);
1440     free(augorig);
1441     free(augorig_canon);
1442     free(augsave);
1443     return 0;
1444  error:
1445     {
1446         const char *emsg =
1447             dyn_err_status == NULL ? err_status : dyn_err_status;
1448         store_error(aug, file_path, meta_path, emsg, errno, NULL, NULL);
1449     }
1450     free(meta_path);
1451     free(augorig);
1452     free(augorig_canon);
1453     free(augsave);
1454     free(dyn_err_status);
1455     return -1;
1456 }
1457
1458 /*
1459  * Local variables:
1460  *  indent-tabs-mode: nil
1461  *  c-indent-level: 4
1462  *  c-basic-offset: 4
1463  *  tab-width: 4
1464  * End:
1465  */