Imported Upstream version 1.12.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 occurred (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 struct info*
552 make_lns_info(struct augeas *aug, const char *filename,
553               const char *text, int text_len) {
554     struct info *info = NULL;
555
556     make_ref(info);
557     ERR_NOMEM(info == NULL, aug);
558
559     if (filename != NULL) {
560         make_ref(info->filename);
561         ERR_NOMEM(info->filename == NULL, aug);
562         info->filename->str = strdup(filename);
563     }
564
565     info->first_line = 1;
566     info->last_line = 1;
567     info->first_column = 1;
568     if (text != NULL) {
569         info->last_column = text_len;
570     }
571
572     info->error = aug->error;
573
574     return info;
575  error:
576     unref(info, info);
577     return NULL;
578 }
579
580 /*
581  * Do the bookkeeping around calling lns_get that is common to load_file
582  * and text_store, in particular, make sure the tree we read gets put into
583  * the right place in AUG and that the span for that tree gets set.
584  *
585  * Transform TEXT using LENS and put the resulting tree at PATH. Use
586  * FILENAME in error messages to indicate where the TEXT came from.
587  */
588 static void lens_get(struct augeas *aug,
589                      struct lens *lens,
590                      const char *filename,
591                      const char *text, int text_len,
592                      const char *path,
593                      struct lns_error **err) {
594     struct info *info = NULL;
595     struct span *span = NULL;
596     struct tree *tree = NULL;
597
598     info = make_lns_info(aug, filename, text, text_len);
599     ERR_BAIL(aug);
600
601     if (aug->flags & AUG_ENABLE_SPAN) {
602         /* Allocate the span already to capture a reference to
603            info->filename */
604         span = make_span(info);
605         ERR_NOMEM(span == NULL, info);
606     }
607
608     tree = lns_get(info, lens, text, aug->flags & AUG_ENABLE_SPAN, err);
609
610     if (*err == NULL) {
611         // Successful get
612         tree_freplace(aug, path, tree);
613         ERR_BAIL(aug);
614
615         /* top level node span entire file length */
616         if (span != NULL && tree != NULL) {
617             tree->parent->span = move(span);
618             tree->parent->span->span_start = 0;
619             tree->parent->span->span_end = text_len;
620         }
621         tree = NULL;
622     }
623  error:
624     free_span(span);
625     unref(info, info);
626     free_tree(tree);
627 }
628
629 static int load_file(struct augeas *aug, struct lens *lens,
630                      const char *lens_name, char *filename) {
631     char *text = NULL;
632     const char *err_status = NULL;
633     char *path = NULL;
634     struct lns_error *err = NULL;
635     int result = -1, r, text_len = 0;
636
637     path = file_name_path(aug, filename);
638     ERR_NOMEM(path == NULL, aug);
639
640     r = add_file_info(aug, path, lens, lens_name, filename, false);
641     if (r < 0)
642         goto done;
643
644     text = xread_file(filename);
645     if (text == NULL) {
646         err_status = "read_failed";
647         goto done;
648     }
649     text_len = strlen(text);
650     text = append_newline(text, text_len);
651
652     lens_get(aug, lens, filename, text, text_len, path, &err);
653     if (err != NULL) {
654         err_status = "parse_failed";
655         goto done;
656     }
657     ERR_BAIL(aug);
658
659     result = 0;
660  done:
661     store_error(aug, filename + strlen(aug->root) - 1, path, err_status,
662                 errno, err, text);
663  error:
664     free_lns_error(err);
665     free(path);
666     free(text);
667     return result;
668 }
669
670 /* The lens for a transform can be referred to in one of two ways:
671  * either by a fully qualified name "Module.lens" or by the special
672  * syntax "@Module"; the latter means we should take the lens from the
673  * autoload transform for Module
674  */
675 static struct lens *lens_from_name(struct augeas *aug, const char *name) {
676     struct lens *result = NULL;
677
678     if (name[0] == '@') {
679         struct module *modl = NULL;
680         for (modl = aug->modules;
681              modl != NULL && !streqv(modl->name, name + 1);
682              modl = modl->next);
683         ERR_THROW(modl == NULL, aug, AUG_ENOLENS,
684                   "Could not find module %s", name + 1);
685         ERR_THROW(modl->autoload == NULL, aug, AUG_ENOLENS,
686                   "No autoloaded lens in module %s", name + 1);
687         result = modl->autoload->lens;
688     } else {
689         result = lens_lookup(aug, name);
690     }
691     ERR_THROW(result == NULL, aug, AUG_ENOLENS,
692               "Can not find lens %s", name);
693     return result;
694  error:
695     return NULL;
696 }
697
698 int text_store(struct augeas *aug, const char *lens_path,
699                const char *path, const char *text) {
700     struct lns_error *err = NULL;
701     int result = -1;
702     const char *err_status = NULL;
703     struct lens *lens = NULL;
704
705     lens = lens_from_name(aug, lens_path);
706     ERR_BAIL(aug);
707
708     lens_get(aug, lens, path, text, strlen(text), path, &err);
709     if (err != NULL) {
710         err_status = "parse_failed";
711         goto error;
712     }
713     ERR_BAIL(aug);
714
715     result = 0;
716  error:
717     store_error(aug, NULL, path, err_status, errno, err, text);
718     free_lns_error(err);
719     return result;
720 }
721
722 const char *xfm_lens_name(struct tree *xfm) {
723     struct tree *l = tree_child(xfm, s_lens);
724
725     if (l == NULL)
726         return "(unknown)";
727     if (l->value == NULL)
728         return "(noname)";
729     return l->value;
730 }
731
732 struct lens *xfm_lens(struct augeas *aug,
733                       struct tree *xfm, const char **lens_name) {
734     struct tree *l = NULL;
735
736     if (lens_name != NULL)
737         *lens_name = NULL;
738
739     for (l = xfm->children;
740          l != NULL && !streqv("lens", l->label);
741          l = l->next);
742
743     if (l == NULL || l->value == NULL)
744         return NULL;
745     if (lens_name != NULL)
746         *lens_name = l->value;
747
748     return lens_from_name(aug, l->value);
749 }
750
751 static void xfm_error(struct tree *xfm, const char *msg) {
752     char *v = msg ? strdup(msg) : NULL;
753     char *l = strdup("error");
754
755     if (l == NULL || v == NULL) {
756         free(v);
757         free(l);
758         return;
759     }
760     tree_append(xfm, l, v);
761 }
762
763 int transform_validate(struct augeas *aug, struct tree *xfm) {
764     struct tree *l = NULL;
765
766     for (struct tree *t = xfm->children; t != NULL; ) {
767         if (streqv(t->label, "lens")) {
768             l = t;
769         } else if ((is_incl(t) || (is_excl(t) && strchr(t->value, SEP) != NULL))
770                        && t->value[0] != SEP) {
771             /* Normalize relative paths to absolute ones */
772             int r;
773             r = REALLOC_N(t->value, strlen(t->value) + 2);
774             ERR_NOMEM(r < 0, aug);
775             memmove(t->value + 1, t->value, strlen(t->value) + 1);
776             t->value[0] = SEP;
777         }
778
779         if (streqv(t->label, "error")) {
780             struct tree *del = t;
781             t = del->next;
782             tree_unlink(aug, del);
783         } else {
784             t = t->next;
785         }
786     }
787
788     if (l == NULL) {
789         xfm_error(xfm, "missing a child with label 'lens'");
790         return -1;
791     }
792     if (l->value == NULL) {
793         xfm_error(xfm, "the 'lens' node does not contain a lens name");
794         return -1;
795     }
796     lens_from_name(aug, l->value);
797     ERR_BAIL(aug);
798
799     return 0;
800  error:
801     xfm_error(xfm, aug->error->details);
802     /* We recorded this error in the tree, clear it so that future
803      * operations report this exact same error (against the wrong lens) */
804     reset_error(aug->error);
805     return -1;
806 }
807
808 void transform_file_error(struct augeas *aug, const char *status,
809                           const char *filename, const char *format, ...) {
810     char *ep = err_path(filename);
811     struct tree *err;
812     char *msg;
813     va_list ap;
814     int r;
815
816     err = tree_fpath_cr(aug, ep);
817     FREE(ep);
818     if (err == NULL)
819         return;
820
821     tree_unlink_children(aug, err);
822     tree_set_value(err, status);
823
824     err = tree_child_cr(err, s_message);
825     if (err == NULL)
826         return;
827
828     va_start(ap, format);
829     r = vasprintf(&msg, format, ap);
830     va_end(ap);
831     if (r < 0)
832         return;
833     tree_set_value(err, msg);
834     free(msg);
835 }
836
837 static struct tree *file_info(struct augeas *aug, const char *fname) {
838     char *path = NULL;
839     struct tree *result = NULL;
840     int r;
841
842     r = pathjoin(&path, 2, AUGEAS_META_FILES, fname);
843     ERR_NOMEM(r < 0, aug);
844
845     result = tree_fpath(aug, path);
846     ERR_BAIL(aug);
847  error:
848     free(path);
849     return result;
850 }
851
852 int transform_load(struct augeas *aug, struct tree *xfm, const char *file) {
853     int nmatches = 0;
854     char **matches;
855     const char *lens_name;
856     struct lens *lens = xfm_lens(aug, xfm, &lens_name);
857     int r;
858
859     if (lens == NULL) {
860         // FIXME: Record an error and return 0
861         return -1;
862     }
863
864     r = filter_generate(xfm, aug->root, &nmatches, &matches);
865     if (r == -1)
866         return -1;
867     for (int i=0; i < nmatches; i++) {
868         const char *filename = matches[i] + strlen(aug->root) - 1;
869         struct tree *finfo = file_info(aug, filename);
870
871         if (file != NULL && STRNEQ(filename, file)) {
872             FREE(matches[i]);
873             continue;
874         }
875
876         if (finfo != NULL && !finfo->dirty &&
877             tree_child(finfo, s_lens) != NULL) {
878             /* We have a potential conflict: since FINFO is not marked as
879                dirty (see aug_load for how the dirty flag on nodes under
880                /augeas/files is used during loading), we already processed
881                it with another lens. The other lens is recorded in
882                FINFO. If it so happens that the lenses are actually the
883                same, we silently move on, as this duplication does no
884                harm. If they are different we definitely have a problem and
885                need to record an error and remove the work the first lens
886                did. */
887             const char *s = xfm_lens_name(finfo);
888             struct lens *other_lens = lens_from_name(aug, s);
889             if (lens != other_lens) {
890                 char *fpath = file_name_path(aug, matches[i]);
891                 transform_file_error(aug, "mxfm_load", filename,
892                   "Lenses %s and %s could be used to load this file",
893                                      s, lens_name);
894                 aug_rm(aug, fpath);
895                 free(fpath);
896             }
897         } else if (!file_current(aug, matches[i], finfo)) {
898             load_file(aug, lens, lens_name, matches[i]);
899         }
900         if (finfo != NULL)
901             finfo->dirty = 0;
902         FREE(matches[i]);
903     }
904     lens_release(lens);
905     free(matches);
906     return 0;
907 }
908
909 int transform_applies(struct tree *xfm, const char *path) {
910     if (STRNEQLEN(path, AUGEAS_FILES_TREE, strlen(AUGEAS_FILES_TREE))
911         || path[strlen(AUGEAS_FILES_TREE)] != SEP)
912         return 0;
913     return filter_matches(xfm, path + strlen(AUGEAS_FILES_TREE));
914 }
915
916 static int transfer_file_attrs(FILE *from, FILE *to,
917                                const char **err_status) {
918     struct stat st;
919     int ret = 0;
920     int selinux_enabled = (is_selinux_enabled() > 0);
921     security_context_t con = NULL;
922
923     int from_fd;
924     int to_fd = fileno(to);
925
926     if (from == NULL) {
927         *err_status = "replace_from_missing";
928         return -1;
929     }
930
931     from_fd = fileno(from);
932
933     ret = fstat(from_fd, &st);
934     if (ret < 0) {
935         *err_status = "replace_stat";
936         return -1;
937     }
938     if (selinux_enabled) {
939         if (fgetfilecon(from_fd, &con) < 0 && errno != ENOTSUP) {
940             *err_status = "replace_getfilecon";
941             return -1;
942         }
943     }
944
945     if (fchown(to_fd, st.st_uid, st.st_gid) < 0) {
946         *err_status = "replace_chown";
947         return -1;
948     }
949     if (fchmod(to_fd, st.st_mode) < 0) {
950         *err_status = "replace_chmod";
951         return -1;
952     }
953     if (selinux_enabled && con != NULL) {
954         if (fsetfilecon(to_fd, con) < 0 && errno != ENOTSUP) {
955             *err_status = "replace_setfilecon";
956             return -1;
957         }
958         freecon(con);
959     }
960     return 0;
961 }
962
963 /* Try to rename FROM to TO. If that fails with an error other than EXDEV
964  * or EBUSY, return -1. If the failure is EXDEV or EBUSY (which we assume
965  * means that FROM or TO is a bindmounted file), and COPY_IF_RENAME_FAILS
966  * is true, copy the contents of FROM into TO and delete FROM.
967  *
968  * If COPY_IF_RENAME_FAILS and UNLINK_IF_RENAME_FAILS are true, and the above
969  * copy mechanism is used, it will unlink the TO path and open with O_EXCL
970  * to ensure we only copy *from* a bind mount rather than into an attacker's
971  * mount placed at TO (e.g. for .augsave).
972  *
973  * Return 0 on success (either rename succeeded or we copied the contents
974  * over successfully), -1 on failure.
975  */
976 static int clone_file(const char *from, const char *to,
977                       const char **err_status, int copy_if_rename_fails,
978                       int unlink_if_rename_fails) {
979     FILE *from_fp = NULL, *to_fp = NULL;
980     char buf[BUFSIZ];
981     size_t len;
982     int to_fd = -1, to_oflags, r;
983     int result = -1;
984
985     if (rename(from, to) == 0)
986         return 0;
987     if ((errno != EXDEV && errno != EBUSY) || !copy_if_rename_fails) {
988         *err_status = "rename";
989         return -1;
990     }
991
992     /* rename not possible, copy file contents */
993     if (!(from_fp = fopen(from, "r"))) {
994         *err_status = "clone_open_src";
995         goto done;
996     }
997
998     if (unlink_if_rename_fails) {
999         r = unlink(to);
1000         if (r < 0) {
1001             *err_status = "clone_unlink_dst";
1002             goto done;
1003         }
1004     }
1005
1006     to_oflags = unlink_if_rename_fails ? O_EXCL : O_TRUNC;
1007     if ((to_fd = open(to, O_WRONLY|O_CREAT|to_oflags, S_IRUSR|S_IWUSR)) < 0) {
1008         *err_status = "clone_open_dst";
1009         goto done;
1010     }
1011     if (!(to_fp = fdopen(to_fd, "w"))) {
1012         *err_status = "clone_fdopen_dst";
1013         goto done;
1014     }
1015
1016     if (transfer_file_attrs(from_fp, to_fp, err_status) < 0)
1017         goto done;
1018
1019     while ((len = fread(buf, 1, BUFSIZ, from_fp)) > 0) {
1020         if (fwrite(buf, 1, len, to_fp) != len) {
1021             *err_status = "clone_write";
1022             goto done;
1023         }
1024     }
1025     if (ferror(from_fp)) {
1026         *err_status = "clone_read";
1027         goto done;
1028     }
1029     if (fflush(to_fp) != 0) {
1030         *err_status = "clone_flush";
1031         goto done;
1032     }
1033     if (fsync(fileno(to_fp)) < 0) {
1034         *err_status = "clone_sync";
1035         goto done;
1036     }
1037     result = 0;
1038  done:
1039     if (from_fp != NULL)
1040         fclose(from_fp);
1041     if (to_fp != NULL) {
1042         if (fclose(to_fp) != 0) {
1043             *err_status = "clone_fclose_dst";
1044             result = -1;
1045         }
1046     } else if (to_fd >= 0 && close(to_fd) < 0) {
1047         *err_status = "clone_close_dst";
1048         result = -1;
1049     }
1050     if (result != 0)
1051         unlink(to);
1052     if (result == 0)
1053         unlink(from);
1054     return result;
1055 }
1056
1057 static char *strappend(const char *s1, const char *s2) {
1058     size_t len = strlen(s1) + strlen(s2);
1059     char *result = NULL, *p;
1060
1061     if (ALLOC_N(result, len + 1) < 0)
1062         return NULL;
1063
1064     p = stpcpy(result, s1);
1065     stpcpy(p, s2);
1066     return result;
1067 }
1068
1069 static int file_saved_event(struct augeas *aug, const char *path) {
1070     const char *saved = strrchr(AUGEAS_EVENTS_SAVED, SEP) + 1;
1071     struct pathx *px;
1072     struct tree *dummy;
1073     int r;
1074
1075     px = pathx_aug_parse(aug, aug->origin, NULL,
1076                          AUGEAS_EVENTS_SAVED "[last()]", true);
1077     ERR_BAIL(aug);
1078
1079     if (pathx_find_one(px, &dummy) == 1) {
1080         r = tree_insert(px, saved, 0);
1081         if (r < 0)
1082             goto error;
1083     }
1084
1085     if (! tree_set(px, path))
1086         goto error;
1087
1088     free_pathx(px);
1089     return 0;
1090  error:
1091     free_pathx(px);
1092     return -1;
1093 }
1094
1095 /*
1096  * Do the bookkeeping around calling LNS_PUT that's needed to update the
1097  * span after writing a tree to file
1098  */
1099 static void lens_put(struct augeas *aug, const char *filename,
1100                      struct lens *lens, const char *text, struct tree *tree,
1101                      FILE *out, struct lns_error **err) {
1102     struct info *info = NULL;
1103     size_t text_len = strlen(text);
1104     bool with_span = aug->flags & AUG_ENABLE_SPAN;
1105
1106     info = make_lns_info(aug, filename, text, text_len);
1107     ERR_BAIL(aug);
1108
1109     if (with_span) {
1110         if (tree->span == NULL) {
1111             tree->span = make_span(info);
1112             ERR_NOMEM(tree->span == NULL, aug);
1113         }
1114         tree->span->span_start = ftell(out);
1115     }
1116
1117     lns_put(info, out, lens, tree->children, text,
1118             aug->flags & AUG_ENABLE_SPAN, err);
1119
1120     if (with_span) {
1121         tree->span->span_end = ftell(out);
1122     }
1123  error:
1124     unref(info, info);
1125 }
1126
1127 /*
1128  * Save TREE->CHILDREN into the file PATH using the lens from XFORM. Errors
1129  * are noted in the /augeas/files hierarchy in AUG->ORIGIN under
1130  * PATH/error.
1131  *
1132  * Writing the file happens by first writing into a temp file, transferring all
1133  * file attributes of PATH to the temp file, and then renaming the temp file
1134  * back to PATH.
1135  *
1136  * Temp files are created alongside the destination file to enable the rename,
1137  * which may be the canonical path (PATH_canon) if PATH is a symlink.
1138  *
1139  * If the AUG_SAVE_NEWFILE flag is set, instead rename to PATH.augnew rather
1140  * than PATH.  If AUG_SAVE_BACKUP is set, move the original to PATH.augsave.
1141  * (Always PATH.aug{new,save} irrespective of whether PATH is a symlink.)
1142  *
1143  * If the rename fails, and the entry AUGEAS_COPY_IF_FAILURE exists in
1144  * AUG->ORIGIN, PATH is instead overwritten by copying file contents.
1145  *
1146  * The table below shows the locations for each permutation.
1147  *
1148  * PATH       save flag    temp file           dest file      backup?
1149  * regular    -            PATH.XXXX           PATH           -
1150  * regular    BACKUP       PATH.XXXX           PATH           PATH.augsave
1151  * regular    NEWFILE      PATH.augnew.XXXX    PATH.augnew    -
1152  * symlink    -            PATH_canon.XXXX     PATH_canon     -
1153  * symlink    BACKUP       PATH_canon.XXXX     PATH_canon     PATH.augsave
1154  * symlink    NEWFILE      PATH.augnew.XXXX    PATH.augnew    -
1155  *
1156  * Return 0 on success, -1 on failure.
1157  */
1158 int transform_save(struct augeas *aug, struct tree *xfm,
1159                    const char *path, struct tree *tree) {
1160     int   fd;
1161     FILE *fp = NULL, *augorig_canon_fp = NULL;
1162     char *augtemp = NULL, *augnew = NULL, *augorig = NULL, *augsave = NULL;
1163     char *augorig_canon = NULL, *augdest = NULL;
1164     int   augorig_exists;
1165     int   copy_if_rename_fails = 0;
1166     char *text = NULL;
1167     const char *filename = path + strlen(AUGEAS_FILES_TREE) + 1;
1168     const char *err_status = NULL;
1169     char *dyn_err_status = NULL;
1170     struct lns_error *err = NULL;
1171     const char *lens_name;
1172     struct lens *lens = xfm_lens(aug, xfm, &lens_name);
1173     int result = -1, r;
1174     bool force_reload;
1175     struct info *info = NULL;
1176
1177     errno = 0;
1178
1179     if (lens == NULL) {
1180         err_status = "lens_name";
1181         goto done;
1182     }
1183
1184     copy_if_rename_fails =
1185         aug_get(aug, AUGEAS_COPY_IF_RENAME_FAILS, NULL) == 1;
1186
1187     if (asprintf(&augorig, "%s%s", aug->root, filename) == -1) {
1188         augorig = NULL;
1189         goto done;
1190     }
1191
1192     augorig_canon = canonicalize_file_name(augorig);
1193     augorig_exists = 1;
1194     if (augorig_canon == NULL) {
1195         if (errno == ENOENT) {
1196             augorig_canon = augorig;
1197             augorig_exists = 0;
1198         } else {
1199             err_status = "canon_augorig";
1200             goto done;
1201         }
1202     }
1203
1204     if (access(augorig_canon, R_OK) == 0) {
1205         augorig_canon_fp = fopen(augorig_canon, "r");
1206         text = xfread_file(augorig_canon_fp);
1207     } else {
1208         text = strdup("");
1209     }
1210
1211     if (text == NULL) {
1212         err_status = "put_read";
1213         goto done;
1214     }
1215
1216     text = append_newline(text, strlen(text));
1217
1218     /* Figure out where to put the .augnew and temp file. If no .augnew file
1219        then put the temp file next to augorig_canon, else next to .augnew. */
1220     if (aug->flags & AUG_SAVE_NEWFILE) {
1221         if (xasprintf(&augnew, "%s" EXT_AUGNEW, augorig) < 0) {
1222             err_status = "augnew_oom";
1223             goto done;
1224         }
1225         augdest = augnew;
1226     } else {
1227         augdest = augorig_canon;
1228     }
1229
1230     if (xasprintf(&augtemp, "%s.XXXXXX", augdest) < 0) {
1231         err_status = "augtemp_oom";
1232         goto done;
1233     }
1234
1235     // FIXME: We might have to create intermediate directories
1236     // to be able to write augnew, but we have no idea what permissions
1237     // etc. they should get. Just the process default ?
1238     fd = mkstemp(augtemp);
1239     if (fd < 0) {
1240         err_status = "mk_augtemp";
1241         goto done;
1242     }
1243     fp = fdopen(fd, "w");
1244     if (fp == NULL) {
1245         err_status = "open_augtemp";
1246         goto done;
1247     }
1248
1249     if (augorig_exists) {
1250         if (transfer_file_attrs(augorig_canon_fp, fp, &err_status) != 0) {
1251             goto done;
1252         }
1253     } else {
1254         /* Since mkstemp is used, the temp file will have secure permissions
1255          * instead of those implied by umask, so change them for new files */
1256         mode_t curumsk = umask(022);
1257         umask(curumsk);
1258
1259         if (fchmod(fileno(fp), 0666 & ~curumsk) < 0) {
1260             err_status = "create_chmod";
1261             goto done;
1262         }
1263     }
1264
1265     if (tree != NULL) {
1266         lens_put(aug, augorig_canon, lens, text, tree, fp, &err);
1267         ERR_BAIL(aug);
1268     }
1269
1270     if (ferror(fp)) {
1271         err_status = "error_augtemp";
1272         goto done;
1273     }
1274
1275     if (fflush(fp) != 0) {
1276         err_status = "flush_augtemp";
1277         goto done;
1278     }
1279
1280     if (fsync(fileno(fp)) < 0) {
1281         err_status = "sync_augtemp";
1282         goto done;
1283     }
1284
1285     if (fclose(fp) != 0) {
1286         err_status = "close_augtemp";
1287         fp = NULL;
1288         goto done;
1289     }
1290
1291     fp = NULL;
1292
1293     if (err != NULL) {
1294         err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
1295         unlink(augtemp);
1296         goto done;
1297     }
1298
1299     {
1300         char *new_text = xread_file(augtemp);
1301         int same = 0;
1302         if (new_text == NULL) {
1303             err_status = "read_augtemp";
1304             goto done;
1305         }
1306         same = STREQ(text, new_text);
1307         FREE(new_text);
1308         if (same) {
1309             result = 0;
1310             unlink(augtemp);
1311             goto done;
1312         } else if (aug->flags & AUG_SAVE_NOOP) {
1313             result = 1;
1314             unlink(augtemp);
1315             goto done;
1316         }
1317     }
1318
1319     if (!(aug->flags & AUG_SAVE_NEWFILE)) {
1320         if (augorig_exists && (aug->flags & AUG_SAVE_BACKUP)) {
1321             r = xasprintf(&augsave, "%s" EXT_AUGSAVE, augorig);
1322             if (r == -1) {
1323                 augsave = NULL;
1324                 goto done;
1325             }
1326
1327             r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
1328             if (r != 0) {
1329                 dyn_err_status = strappend(err_status, "_augsave");
1330                 goto done;
1331             }
1332         }
1333     }
1334
1335     r = clone_file(augtemp, augdest, &err_status, copy_if_rename_fails, 0);
1336     if (r != 0) {
1337         unlink(augtemp);
1338         dyn_err_status = strappend(err_status, "_augtemp");
1339         goto done;
1340     }
1341
1342     result = 1;
1343
1344  done:
1345     force_reload = aug->flags & AUG_SAVE_NEWFILE;
1346     r = add_file_info(aug, path, lens, lens_name, augorig, force_reload);
1347     if (r < 0) {
1348         err_status = "file_info";
1349         result = -1;
1350     }
1351     if (result > 0) {
1352         r = file_saved_event(aug, path);
1353         if (r < 0) {
1354             err_status = "saved_event";
1355             result = -1;
1356         }
1357     }
1358     {
1359         const char *emsg =
1360             dyn_err_status == NULL ? err_status : dyn_err_status;
1361         store_error(aug, filename, path, emsg, errno, err, text);
1362     }
1363  error:
1364     free(dyn_err_status);
1365     lens_release(lens);
1366     free(text);
1367     free(augtemp);
1368     free(augnew);
1369     if (augorig_canon != augorig)
1370         free(augorig_canon);
1371     free(augorig);
1372     free(augsave);
1373     free_lns_error(err);
1374     unref(info, info);
1375
1376     if (fp != NULL)
1377         fclose(fp);
1378     if (augorig_canon_fp != NULL)
1379         fclose(augorig_canon_fp);
1380     return result;
1381 }
1382
1383 int text_retrieve(struct augeas *aug, const char *lens_name,
1384                   const char *path, struct tree *tree,
1385                   const char *text_in, char **text_out) {
1386     struct memstream ms;
1387     bool ms_open = false;
1388     const char *err_status = NULL;
1389     struct lns_error *err = NULL;
1390     struct lens *lens = NULL;
1391     int result = -1, r;
1392     struct info *info = NULL;
1393
1394     MEMZERO(&ms, 1);
1395     errno = 0;
1396
1397     lens = lens_from_name(aug, lens_name);
1398     if (lens == NULL) {
1399         err_status = "lens_name";
1400         goto done;
1401     }
1402
1403     r = init_memstream(&ms);
1404     if (r < 0) {
1405         err_status = "init_memstream";
1406         goto done;
1407     }
1408     ms_open = true;
1409
1410     if (tree != NULL) {
1411         lens_put(aug, path, lens, text_in, tree, ms.stream, &err);
1412         ERR_BAIL(aug);
1413     }
1414
1415     r = close_memstream(&ms);
1416     ms_open = false;
1417     if (r < 0) {
1418         err_status = "close_memstream";
1419         goto done;
1420     }
1421
1422     *text_out = ms.buf;
1423     ms.buf = NULL;
1424
1425     if (err != NULL) {
1426         err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
1427         goto done;
1428     }
1429
1430     result = 0;
1431
1432  done:
1433     store_error(aug, NULL, path, err_status, errno, err, text_in);
1434  error:
1435     lens_release(lens);
1436     if (result < 0) {
1437         free(*text_out);
1438         *text_out = NULL;
1439     }
1440     free_lns_error(err);
1441     unref(info, info);
1442
1443     if (ms_open)
1444         close_memstream(&ms);
1445     return result;
1446 }
1447
1448 int remove_file(struct augeas *aug, struct tree *tree) {
1449     const char *err_status = NULL;
1450     char *dyn_err_status = NULL;
1451     char *augsave = NULL, *augorig = NULL, *augorig_canon = NULL;
1452     struct tree *path = NULL;
1453     const char *file_path = NULL;
1454     char *meta_path = NULL;
1455     int r;
1456
1457     path = tree_child(tree, s_path);
1458     if (path == NULL) {
1459         err_status = "no child called 'path' for file entry";
1460         goto error;
1461     }
1462     file_path = path->value + strlen(AUGEAS_FILES_TREE);
1463     path = NULL;
1464
1465     meta_path = path_of_tree(tree);
1466     if (meta_path == NULL) {
1467         err_status = "path_of_tree";
1468         goto error;
1469     }
1470
1471     if ((augorig = strappend(aug->root, file_path)) == NULL) {
1472         err_status = "root_file";
1473         goto error;
1474     }
1475
1476     augorig_canon = canonicalize_file_name(augorig);
1477     if (augorig_canon == NULL) {
1478         if (errno == ENOENT) {
1479             goto done;
1480         } else {
1481             err_status = "canon_augorig";
1482             goto error;
1483         }
1484     }
1485
1486     r = file_saved_event(aug, meta_path + strlen(AUGEAS_META_TREE));
1487     if (r < 0) {
1488         err_status = "saved_event";
1489         goto error;
1490     }
1491
1492     if (aug->flags & AUG_SAVE_NOOP)
1493         goto done;
1494
1495     if (aug->flags & AUG_SAVE_BACKUP) {
1496         /* Move file to one with extension .augsave */
1497         r = asprintf(&augsave, "%s" EXT_AUGSAVE, augorig_canon);
1498         if (r == -1) {
1499             augsave = NULL;
1500                 goto error;
1501         }
1502
1503         r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
1504         if (r != 0) {
1505             dyn_err_status = strappend(err_status, "_augsave");
1506             goto error;
1507         }
1508     } else {
1509         /* Unlink file */
1510         r = unlink(augorig_canon);
1511         if (r < 0) {
1512             err_status = "unlink_orig";
1513             goto error;
1514         }
1515     }
1516     path = NULL;
1517     tree_unlink(aug, tree);
1518  done:
1519     free(meta_path);
1520     free(augorig);
1521     free(augorig_canon);
1522     free(augsave);
1523     return 0;
1524  error:
1525     {
1526         const char *emsg =
1527             dyn_err_status == NULL ? err_status : dyn_err_status;
1528         store_error(aug, file_path, meta_path, emsg, errno, NULL, NULL);
1529     }
1530     free(meta_path);
1531     free(augorig);
1532     free(augorig_canon);
1533     free(augsave);
1534     free(dyn_err_status);
1535     return -1;
1536 }
1537
1538 /*
1539  * Local variables:
1540  *  indent-tabs-mode: nil
1541  *  c-indent-level: 4
1542  *  c-basic-offset: 4
1543  *  tab-width: 4
1544  * End:
1545  */