1f80134818547f4a376380cf6a114902cddc526f
[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     tree_unlink_children(aug, parent);
544     list_append(parent->children, sub);
545     list_for_each(s, sub) {
546         s->parent = parent;
547     }
548 }
549
550 static int load_file(struct augeas *aug, struct lens *lens,
551                      const char *lens_name, char *filename) {
552     char *text = NULL;
553     const char *err_status = NULL;
554     struct tree *tree = NULL;
555     char *path = 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;
560
561     path = file_name_path(aug, filename);
562     ERR_NOMEM(path == NULL, aug);
563
564     r = add_file_info(aug, path, lens, lens_name, filename, false);
565     if (r < 0)
566         goto done;
567
568     text = xread_file(filename);
569     if (text == NULL) {
570         err_status = "read_failed";
571         goto done;
572     }
573     text_len = strlen(text);
574     text = append_newline(text, text_len);
575
576     make_ref(info);
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;
582
583     if (aug->flags & AUG_ENABLE_SPAN) {
584         /* Allocate the span already to capture a reference to
585            info->filename */
586         span = make_span(info);
587         ERR_NOMEM(span == NULL, info);
588     }
589
590     tree = lns_get(info, lens, text, &err);
591
592     if (err != NULL) {
593         err_status = "parse_failed";
594         goto done;
595     }
596
597     tree_freplace(aug, path, tree);
598     ERR_BAIL(aug);
599
600     /* top level node span entire file length */
601     if (span != NULL && tree != NULL) {
602         tree->parent->span = span;
603         span = NULL;
604         tree->parent->span->span_start = 0;
605         tree->parent->span->span_end = text_len;
606     }
607
608     tree = NULL;
609
610     result = 0;
611  done:
612     store_error(aug, filename + strlen(aug->root) - 1, path, err_status,
613                 errno, err, text);
614  error:
615     unref(info, info);
616     free_lns_error(err);
617     free(path);
618     free_span(span);
619     free_tree(tree);
620     free(text);
621     return result;
622 }
623
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
628  */
629 static struct lens *lens_from_name(struct augeas *aug, const char *name) {
630     struct lens *result = NULL;
631
632     if (name[0] == '@') {
633         struct module *modl = NULL;
634         for (modl = aug->modules;
635              modl != NULL && !streqv(modl->name, name + 1);
636              modl = modl->next);
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;
642     } else {
643         result = lens_lookup(aug, name);
644     }
645     ERR_THROW(result == NULL, aug, AUG_ENOLENS,
646               "Can not find lens %s", name);
647     return result;
648  error:
649     return NULL;
650 }
651
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;
657     int result = -1;
658     const char *err_status = NULL;
659     struct lens *lens = NULL;
660
661     lens = lens_from_name(aug, lens_path);
662     ERR_BAIL(aug);
663
664     make_ref(info);
665     info->first_line = 1;
666     info->last_line = 1;
667     info->first_column = 1;
668     info->last_column = strlen(text);
669
670     tree = lns_get(info, lens, text, &err);
671     if (err != NULL) {
672         err_status = "parse_failed";
673         goto error;
674     }
675
676     tree_freplace(aug, path, tree);
677     ERR_BAIL(aug);
678
679     tree = NULL;
680
681     result = 0;
682  error:
683     unref(info, info);
684     store_error(aug, NULL, path, err_status, errno, err, text);
685     free_tree(tree);
686     free_lns_error(err);
687     return result;
688 }
689
690 const char *xfm_lens_name(struct tree *xfm) {
691     struct tree *l = tree_child(xfm, s_lens);
692
693     if (l == NULL)
694         return "(unknown)";
695     if (l->value == NULL)
696         return "(noname)";
697     return l->value;
698 }
699
700 struct lens *xfm_lens(struct augeas *aug,
701                       struct tree *xfm, const char **lens_name) {
702     struct tree *l = NULL;
703
704     for (l = xfm->children;
705          l != NULL && !streqv("lens", l->label);
706          l = l->next);
707
708     if (l == NULL || l->value == NULL)
709         return NULL;
710     if (lens_name != NULL)
711         *lens_name = l->value;
712
713     return lens_from_name(aug, l->value);
714 }
715
716 static void xfm_error(struct tree *xfm, const char *msg) {
717     char *v = msg ? strdup(msg) : NULL;
718     char *l = strdup("error");
719
720     if (l == NULL || v == NULL) {
721         free(v);
722         free(l);
723         return;
724     }
725     tree_append(xfm, l, v);
726 }
727
728 int transform_validate(struct augeas *aug, struct tree *xfm) {
729     struct tree *l = NULL;
730
731     for (struct tree *t = xfm->children; t != NULL; ) {
732         if (streqv(t->label, "lens")) {
733             l = t;
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 */
737             int r;
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);
741             t->value[0] = SEP;
742         }
743
744         if (streqv(t->label, "error")) {
745             struct tree *del = t;
746             t = del->next;
747             tree_unlink(aug, del);
748         } else {
749             t = t->next;
750         }
751     }
752
753     if (l == NULL) {
754         xfm_error(xfm, "missing a child with label 'lens'");
755         return -1;
756     }
757     if (l->value == NULL) {
758         xfm_error(xfm, "the 'lens' node does not contain a lens name");
759         return -1;
760     }
761     lens_from_name(aug, l->value);
762     ERR_BAIL(aug);
763
764     return 0;
765  error:
766     xfm_error(xfm, aug->error->details);
767     return -1;
768 }
769
770 void transform_file_error(struct augeas *aug, const char *status,
771                           const char *filename, const char *format, ...) {
772     char *ep = err_path(filename);
773     struct tree *err;
774     char *msg;
775     va_list ap;
776     int r;
777
778     err = tree_fpath_cr(aug, ep);
779     FREE(ep);
780     if (err == NULL)
781         return;
782
783     tree_unlink_children(aug, err);
784     tree_set_value(err, status);
785
786     err = tree_child_cr(err, s_message);
787     if (err == NULL)
788         return;
789
790     va_start(ap, format);
791     r = vasprintf(&msg, format, ap);
792     va_end(ap);
793     if (r < 0)
794         return;
795     tree_set_value(err, msg);
796     free(msg);
797 }
798
799 static struct tree *file_info(struct augeas *aug, const char *fname) {
800     char *path = NULL;
801     struct tree *result = NULL;
802     int r;
803
804     r = pathjoin(&path, 2, AUGEAS_META_FILES, fname);
805     ERR_NOMEM(r < 0, aug);
806
807     result = tree_fpath(aug, path);
808     ERR_BAIL(aug);
809  error:
810     free(path);
811     return result;
812 }
813
814 int transform_load(struct augeas *aug, struct tree *xfm, const char *file) {
815     int nmatches = 0;
816     char **matches;
817     const char *lens_name;
818     struct lens *lens = xfm_lens(aug, xfm, &lens_name);
819     int r;
820
821     if (lens == NULL) {
822         // FIXME: Record an error and return 0
823         return -1;
824     }
825
826     r = filter_generate(xfm, aug->root, &nmatches, &matches);
827     if (r == -1)
828         return -1;
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);
832
833         if (file != NULL && STRNEQ(filename, file)) {
834             FREE(matches[i]);
835             continue;
836         }
837
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
848                did. */
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",
855                                      s, lens_name);
856                 aug_rm(aug, fpath);
857                 free(fpath);
858             }
859         } else if (!file_current(aug, matches[i], finfo)) {
860             load_file(aug, lens, lens_name, matches[i]);
861         }
862         if (finfo != NULL)
863             finfo->dirty = 0;
864         FREE(matches[i]);
865     }
866     lens_release(lens);
867     free(matches);
868     return 0;
869 }
870
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)
874         return 0;
875     return filter_matches(xfm, path + strlen(AUGEAS_FILES_TREE));
876 }
877
878 static int transfer_file_attrs(FILE *from, FILE *to,
879                                const char **err_status) {
880     struct stat st;
881     int ret = 0;
882     int selinux_enabled = (is_selinux_enabled() > 0);
883     security_context_t con = NULL;
884
885     int from_fd;
886     int to_fd = fileno(to);
887
888     if (from == NULL) {
889         *err_status = "replace_from_missing";
890         return -1;
891     }
892
893     from_fd = fileno(from);
894
895     ret = fstat(from_fd, &st);
896     if (ret < 0) {
897         *err_status = "replace_stat";
898         return -1;
899     }
900     if (selinux_enabled) {
901         if (fgetfilecon(from_fd, &con) < 0 && errno != ENOTSUP) {
902             *err_status = "replace_getfilecon";
903             return -1;
904         }
905     }
906
907     if (fchown(to_fd, st.st_uid, st.st_gid) < 0) {
908         *err_status = "replace_chown";
909         return -1;
910     }
911     if (fchmod(to_fd, st.st_mode) < 0) {
912         *err_status = "replace_chmod";
913         return -1;
914     }
915     if (selinux_enabled && con != NULL) {
916         if (fsetfilecon(to_fd, con) < 0 && errno != ENOTSUP) {
917             *err_status = "replace_setfilecon";
918             return -1;
919         }
920         freecon(con);
921     }
922     return 0;
923 }
924
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.
929  *
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).
934  *
935  * Return 0 on success (either rename succeeded or we copied the contents
936  * over successfully), -1 on failure.
937  */
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;
942     char buf[BUFSIZ];
943     size_t len;
944     int to_fd = -1, to_oflags, r;
945     int result = -1;
946
947     if (rename(from, to) == 0)
948         return 0;
949     if ((errno != EXDEV && errno != EBUSY) || !copy_if_rename_fails) {
950         *err_status = "rename";
951         return -1;
952     }
953
954     /* rename not possible, copy file contents */
955     if (!(from_fp = fopen(from, "r"))) {
956         *err_status = "clone_open_src";
957         goto done;
958     }
959
960     if (unlink_if_rename_fails) {
961         r = unlink(to);
962         if (r < 0) {
963             *err_status = "clone_unlink_dst";
964             goto done;
965         }
966     }
967
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";
971         goto done;
972     }
973     if (!(to_fp = fdopen(to_fd, "w"))) {
974         *err_status = "clone_fdopen_dst";
975         goto done;
976     }
977
978     if (transfer_file_attrs(from_fp, to_fp, err_status) < 0)
979         goto done;
980
981     while ((len = fread(buf, 1, BUFSIZ, from_fp)) > 0) {
982         if (fwrite(buf, 1, len, to_fp) != len) {
983             *err_status = "clone_write";
984             goto done;
985         }
986     }
987     if (ferror(from_fp)) {
988         *err_status = "clone_read";
989         goto done;
990     }
991     if (fflush(to_fp) != 0) {
992         *err_status = "clone_flush";
993         goto done;
994     }
995     if (fsync(fileno(to_fp)) < 0) {
996         *err_status = "clone_sync";
997         goto done;
998     }
999     result = 0;
1000  done:
1001     if (from_fp != NULL)
1002         fclose(from_fp);
1003     if (to_fp != NULL) {
1004         if (fclose(to_fp) != 0) {
1005             *err_status = "clone_fclose_dst";
1006             result = -1;
1007         }
1008     } else if (to_fd >= 0 && close(to_fd) < 0) {
1009         *err_status = "clone_close_dst";
1010         result = -1;
1011     }
1012     if (result != 0)
1013         unlink(to);
1014     if (result == 0)
1015         unlink(from);
1016     return result;
1017 }
1018
1019 static char *strappend(const char *s1, const char *s2) {
1020     size_t len = strlen(s1) + strlen(s2);
1021     char *result = NULL, *p;
1022
1023     if (ALLOC_N(result, len + 1) < 0)
1024         return NULL;
1025
1026     p = stpcpy(result, s1);
1027     stpcpy(p, s2);
1028     return result;
1029 }
1030
1031 static int file_saved_event(struct augeas *aug, const char *path) {
1032     const char *saved = strrchr(AUGEAS_EVENTS_SAVED, SEP) + 1;
1033     struct pathx *px;
1034     struct tree *dummy;
1035     int r;
1036
1037     px = pathx_aug_parse(aug, aug->origin, NULL,
1038                          AUGEAS_EVENTS_SAVED "[last()]", true);
1039     ERR_BAIL(aug);
1040
1041     if (pathx_find_one(px, &dummy) == 1) {
1042         r = tree_insert(px, saved, 0);
1043         if (r < 0)
1044             goto error;
1045     }
1046
1047     if (! tree_set(px, path))
1048         goto error;
1049
1050     free_pathx(px);
1051     return 0;
1052  error:
1053     free_pathx(px);
1054     return -1;
1055 }
1056
1057 /*
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
1060  * PATH/error.
1061  *
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
1064  * back to PATH.
1065  *
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.
1068  *
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.)
1072  *
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.
1075  *
1076  * The table below shows the locations for each permutation.
1077  *
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    -
1085  *
1086  * Return 0 on success, -1 on failure.
1087  */
1088 int transform_save(struct augeas *aug, struct tree *xfm,
1089                    const char *path, struct tree *tree) {
1090     int   fd;
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;
1094     int   augorig_exists;
1095     int   copy_if_rename_fails = 0;
1096     char *text = NULL;
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);
1103     int result = -1, r;
1104     bool force_reload;
1105
1106     errno = 0;
1107
1108     if (lens == NULL) {
1109         err_status = "lens_name";
1110         goto done;
1111     }
1112
1113     copy_if_rename_fails =
1114         aug_get(aug, AUGEAS_COPY_IF_RENAME_FAILS, NULL) == 1;
1115
1116     if (asprintf(&augorig, "%s%s", aug->root, filename) == -1) {
1117         augorig = NULL;
1118         goto done;
1119     }
1120
1121     augorig_canon = canonicalize_file_name(augorig);
1122     augorig_exists = 1;
1123     if (augorig_canon == NULL) {
1124         if (errno == ENOENT) {
1125             augorig_canon = augorig;
1126             augorig_exists = 0;
1127         } else {
1128             err_status = "canon_augorig";
1129             goto done;
1130         }
1131     }
1132
1133     if (access(augorig_canon, R_OK) == 0) {
1134         augorig_canon_fp = fopen(augorig_canon, "r");
1135         text = xfread_file(augorig_canon_fp);
1136     } else {
1137         text = strdup("");
1138     }
1139
1140     if (text == NULL) {
1141         err_status = "put_read";
1142         goto done;
1143     }
1144
1145     text = append_newline(text, strlen(text));
1146
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";
1152             goto done;
1153         }
1154         augdest = augnew;
1155     } else {
1156         augdest = augorig_canon;
1157     }
1158
1159     if (xasprintf(&augtemp, "%s.XXXXXX", augdest) < 0) {
1160         err_status = "augtemp_oom";
1161         goto done;
1162     }
1163
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);
1168     if (fd < 0) {
1169         err_status = "mk_augtemp";
1170         goto done;
1171     }
1172     fp = fdopen(fd, "w");
1173     if (fp == NULL) {
1174         err_status = "open_augtemp";
1175         goto done;
1176     }
1177
1178     if (augorig_exists) {
1179         if (transfer_file_attrs(augorig_canon_fp, fp, &err_status) != 0) {
1180             goto done;
1181         }
1182     } else {
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);
1186         umask(curumsk);
1187
1188         if (fchmod(fileno(fp), 0666 & ~curumsk) < 0) {
1189             err_status = "create_chmod";
1190             return -1;
1191         }
1192     }
1193
1194     if (tree != NULL)
1195         lns_put(fp, lens, tree->children, text, &err);
1196
1197     if (ferror(fp)) {
1198         err_status = "error_augtemp";
1199         goto done;
1200     }
1201
1202     if (fflush(fp) != 0) {
1203         err_status = "flush_augtemp";
1204         goto done;
1205     }
1206
1207     if (fsync(fileno(fp)) < 0) {
1208         err_status = "sync_augtemp";
1209         goto done;
1210     }
1211
1212     if (fclose(fp) != 0) {
1213         err_status = "close_augtemp";
1214         fp = NULL;
1215         goto done;
1216     }
1217
1218     fp = NULL;
1219
1220     if (err != NULL) {
1221         err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
1222         unlink(augtemp);
1223         goto done;
1224     }
1225
1226     {
1227         char *new_text = xread_file(augtemp);
1228         int same = 0;
1229         if (new_text == NULL) {
1230             err_status = "read_augtemp";
1231             goto done;
1232         }
1233         same = STREQ(text, new_text);
1234         FREE(new_text);
1235         if (same) {
1236             result = 0;
1237             unlink(augtemp);
1238             goto done;
1239         } else if (aug->flags & AUG_SAVE_NOOP) {
1240             result = 1;
1241             unlink(augtemp);
1242             goto done;
1243         }
1244     }
1245
1246     if (!(aug->flags & AUG_SAVE_NEWFILE)) {
1247         if (augorig_exists && (aug->flags & AUG_SAVE_BACKUP)) {
1248             r = xasprintf(&augsave, "%s" EXT_AUGSAVE, augorig);
1249             if (r == -1) {
1250                 augsave = NULL;
1251                 goto done;
1252             }
1253
1254             r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
1255             if (r != 0) {
1256                 dyn_err_status = strappend(err_status, "_augsave");
1257                 goto done;
1258             }
1259         }
1260     }
1261
1262     r = clone_file(augtemp, augdest, &err_status, copy_if_rename_fails, 0);
1263     if (r != 0) {
1264         dyn_err_status = strappend(err_status, "_augtemp");
1265         goto done;
1266     }
1267
1268     result = 1;
1269
1270  done:
1271     force_reload = aug->flags & AUG_SAVE_NEWFILE;
1272     r = add_file_info(aug, path, lens, lens_name, augorig, force_reload);
1273     if (r < 0) {
1274         err_status = "file_info";
1275         result = -1;
1276     }
1277     if (result > 0) {
1278         r = file_saved_event(aug, path);
1279         if (r < 0) {
1280             err_status = "saved_event";
1281             result = -1;
1282         }
1283     }
1284     {
1285         const char *emsg =
1286             dyn_err_status == NULL ? err_status : dyn_err_status;
1287         store_error(aug, filename, path, emsg, errno, err, text);
1288     }
1289     free(dyn_err_status);
1290     lens_release(lens);
1291     free(text);
1292     free(augtemp);
1293     free(augnew);
1294     if (augorig_canon != augorig)
1295         free(augorig_canon);
1296     free(augorig);
1297     free(augsave);
1298     free_lns_error(err);
1299
1300     if (fp != NULL)
1301         fclose(fp);
1302     if (augorig_canon_fp != NULL)
1303         fclose(augorig_canon_fp);
1304     return result;
1305 }
1306
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;
1315     int result = -1, r;
1316
1317     MEMZERO(&ms, 1);
1318     errno = 0;
1319
1320     lens = lens_from_name(aug, lens_name);
1321     if (lens == NULL) {
1322         err_status = "lens_name";
1323         goto done;
1324     }
1325
1326     r = init_memstream(&ms);
1327     if (r < 0) {
1328         err_status = "init_memstream";
1329         goto done;
1330     }
1331     ms_open = true;
1332
1333     if (tree != NULL)
1334         lns_put(ms.stream, lens, tree->children, text_in, &err);
1335
1336     r = close_memstream(&ms);
1337     ms_open = false;
1338     if (r < 0) {
1339         err_status = "close_memstream";
1340         goto done;
1341     }
1342
1343     *text_out = ms.buf;
1344     ms.buf = NULL;
1345
1346     if (err != NULL) {
1347         err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
1348         goto done;
1349     }
1350
1351     result = 0;
1352
1353  done:
1354     store_error(aug, NULL, path, err_status, errno, err, text_in);
1355     lens_release(lens);
1356     if (result < 0) {
1357         free(*text_out);
1358         *text_out = NULL;
1359     }
1360     free_lns_error(err);
1361
1362     if (ms_open)
1363         close_memstream(&ms);
1364     return result;
1365 }
1366
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;
1374     int r;
1375
1376     path = tree_child(tree, s_path);
1377     if (path == NULL) {
1378         err_status = "no child called 'path' for file entry";
1379         goto error;
1380     }
1381     file_path = path->value + strlen(AUGEAS_FILES_TREE);
1382     path = NULL;
1383
1384     meta_path = path_of_tree(tree);
1385     if (meta_path == NULL) {
1386         err_status = "path_of_tree";
1387         goto error;
1388     }
1389
1390     if ((augorig = strappend(aug->root, file_path)) == NULL) {
1391         err_status = "root_file";
1392         goto error;
1393     }
1394
1395     augorig_canon = canonicalize_file_name(augorig);
1396     if (augorig_canon == NULL) {
1397         if (errno == ENOENT) {
1398             goto done;
1399         } else {
1400             err_status = "canon_augorig";
1401             goto error;
1402         }
1403     }
1404
1405     r = file_saved_event(aug, meta_path + strlen(AUGEAS_META_TREE));
1406     if (r < 0) {
1407         err_status = "saved_event";
1408         goto error;
1409     }
1410
1411     if (aug->flags & AUG_SAVE_NOOP)
1412         goto done;
1413
1414     if (aug->flags & AUG_SAVE_BACKUP) {
1415         /* Move file to one with extension .augsave */
1416         r = asprintf(&augsave, "%s" EXT_AUGSAVE, augorig_canon);
1417         if (r == -1) {
1418             augsave = NULL;
1419                 goto error;
1420         }
1421
1422         r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
1423         if (r != 0) {
1424             dyn_err_status = strappend(err_status, "_augsave");
1425             goto error;
1426         }
1427     } else {
1428         /* Unlink file */
1429         r = unlink(augorig_canon);
1430         if (r < 0) {
1431             err_status = "unlink_orig";
1432             goto error;
1433         }
1434     }
1435     path = NULL;
1436     tree_unlink(aug, tree);
1437  done:
1438     free(meta_path);
1439     free(augorig);
1440     free(augorig_canon);
1441     free(augsave);
1442     return 0;
1443  error:
1444     {
1445         const char *emsg =
1446             dyn_err_status == NULL ? err_status : dyn_err_status;
1447         store_error(aug, file_path, meta_path, emsg, errno, NULL, NULL);
1448     }
1449     free(meta_path);
1450     free(augorig);
1451     free(augorig_canon);
1452     free(augsave);
1453     free(dyn_err_status);
1454     return -1;
1455 }
1456
1457 /*
1458  * Local variables:
1459  *  indent-tabs-mode: nil
1460  *  c-indent-level: 4
1461  *  c-basic-offset: 4
1462  *  tab-width: 4
1463  * End:
1464  */