Coverage Report

Created: 2023-09-23 07:04

/src/augeas/src/transform.c
Line
Count
Source (jump to first uncovered line)
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
0
#define EXT_AUGNEW ".augnew"
47
/* Extension for backup files */
48
0
#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
0
struct filter *make_filter(struct string *glb, unsigned int include) {
80
0
    struct filter *f;
81
0
    make_ref(f);
82
0
    f->glob = glb;
83
0
    f->include = include;
84
0
    return f;
85
0
}
86
87
0
void free_filter(struct filter *f) {
88
0
    if (f == NULL)
89
0
        return;
90
0
    assert(f->ref == 0);
91
0
    unref(f->next, filter);
92
0
    unref(f->glob, string);
93
0
    free(f);
94
0
}
95
96
0
static const char *pathbase(const char *path) {
97
0
    const char *p = strrchr(path, SEP);
98
0
    return (p == NULL) ? path : p + 1;
99
0
}
100
101
0
static bool is_excl(struct tree *f) {
102
0
    return streqv(f->label, "excl") && f->value != NULL;
103
0
}
104
105
0
static bool is_incl(struct tree *f) {
106
0
    return streqv(f->label, "incl") && f->value != NULL;
107
0
}
108
109
0
static bool is_regular_file(const char *path) {
110
0
    int r;
111
0
    struct stat st;
112
113
0
    r = stat(path, &st);
114
0
    if (r < 0)
115
0
        return false;
116
0
    return S_ISREG(st.st_mode);
117
0
}
118
119
0
static char *mtime_as_string(struct augeas *aug, const char *fname) {
120
0
    int r;
121
0
    struct stat st;
122
0
    char *result = NULL;
123
124
0
    if (fname == NULL) {
125
0
        result = strdup("0");
126
0
        ERR_NOMEM(result == NULL, aug);
127
0
        goto done;
128
0
    }
129
130
0
    r = stat(fname, &st);
131
0
    if (r < 0) {
132
        /* If we fail to stat, silently ignore the error
133
         * and report an impossible mtime */
134
0
        result = strdup("0");
135
0
        ERR_NOMEM(result == NULL, aug);
136
0
    } else {
137
0
        r = xasprintf(&result, "%ld", (long) st.st_mtime);
138
0
        ERR_NOMEM(r < 0, aug);
139
0
    }
140
0
 done:
141
0
    return result;
142
0
 error:
143
0
    FREE(result);
144
0
    return NULL;
145
0
}
146
147
/* fnmatch(3) which will match // in a pattern to a path, like glob(3) does */
148
0
static int fnmatch_normalize(const char *pattern, const char *string, int flags) {
149
0
    int i, j, r;
150
0
    char *pattern_norm = NULL;
151
152
0
    r = ALLOC_N(pattern_norm, strlen(pattern) + 1);
153
0
    if (r < 0)
154
0
        goto error;
155
156
0
    for (i = 0, j = 0; i < strlen(pattern); i++) {
157
0
        if (pattern[i] != '/' || pattern[i+1] != '/') {
158
0
            pattern_norm[j] = pattern[i];
159
0
            j++;
160
0
        }
161
0
    }
162
0
    pattern_norm[j] = 0;
163
164
0
    r = fnmatch(pattern_norm, string, flags);
165
0
    FREE(pattern_norm);
166
0
    return r;
167
168
0
 error:
169
0
    if (pattern_norm != NULL)
170
0
        FREE(pattern_norm);
171
0
    return -1;
172
0
}
173
174
static bool file_current(struct augeas *aug, const char *fname,
175
0
                         struct tree *finfo) {
176
0
    struct tree *mtime = tree_child(finfo, s_mtime);
177
0
    struct tree *file = NULL, *path = NULL;
178
0
    int r;
179
0
    struct stat st;
180
0
    int64_t mtime_i;
181
182
0
    if (mtime == NULL || mtime->value == NULL)
183
0
        return false;
184
185
0
    r = xstrtoint64(mtime->value, 10, &mtime_i);
186
0
    if (r < 0) {
187
        /* Ignore silently and err on the side of caution */
188
0
        return false;
189
0
    }
190
191
0
    r = stat(fname, &st);
192
0
    if (r < 0)
193
0
        return false;
194
195
0
    if (mtime_i != (int64_t) st.st_mtime)
196
0
        return false;
197
198
0
    path = tree_child(finfo, s_path);
199
0
    if (path == NULL)
200
0
        return false;
201
202
0
    file = tree_fpath(aug, path->value);
203
0
    return (file != NULL && ! file->dirty);
204
0
}
205
206
static int filter_generate(struct tree *xfm, const char *root,
207
0
                           int *nmatches, char ***matches) {
208
0
    glob_t globbuf;
209
0
    int gl_flags = glob_flags;
210
0
    int r;
211
0
    int ret = 0;
212
0
    char **pathv = NULL;
213
0
    int pathc = 0;
214
0
    int root_prefix = strlen(root) - 1;
215
216
0
    *nmatches = 0;
217
0
    *matches = NULL;
218
0
    MEMZERO(&globbuf, 1);
219
220
0
    list_for_each(f, xfm->children) {
221
0
        char *globpat = NULL;
222
0
        if (! is_incl(f))
223
0
            continue;
224
0
        pathjoin(&globpat, 2, root, f->value);
225
0
        r = glob(globpat, gl_flags, NULL, &globbuf);
226
0
        free(globpat);
227
228
0
        if (r != 0 && r != GLOB_NOMATCH)
229
0
            goto error;
230
0
        gl_flags |= GLOB_APPEND;
231
0
    }
232
233
0
    pathc = globbuf.gl_pathc;
234
0
    int pathind = 0;
235
236
0
    if (ALLOC_N(pathv, pathc) < 0)
237
0
        goto error;
238
239
0
    for (int i=0; i < pathc; i++) {
240
0
        const char *path = globbuf.gl_pathv[i] + root_prefix;
241
0
        bool include = true;
242
243
0
        list_for_each(e, xfm->children) {
244
0
            if (! is_excl(e))
245
0
                continue;
246
247
0
            if (strchr(e->value, SEP) == NULL)
248
0
                path = pathbase(path);
249
250
0
            r = fnmatch_normalize(e->value, path, fnm_flags);
251
0
            if (r < 0)
252
0
                goto error;
253
0
            else if (r == 0)
254
0
                include = false;
255
0
        }
256
257
0
        if (include)
258
0
            include = is_regular_file(globbuf.gl_pathv[i]);
259
260
0
        if (include) {
261
0
            pathv[pathind] = strdup(globbuf.gl_pathv[i]);
262
0
            if (pathv[pathind] == NULL)
263
0
                goto error;
264
0
            pathind += 1;
265
0
        }
266
0
    }
267
0
    pathc = pathind;
268
269
0
    if (REALLOC_N(pathv, pathc) == -1)
270
0
        goto error;
271
272
0
    *matches = pathv;
273
0
    *nmatches = pathc;
274
0
 done:
275
0
    globfree(&globbuf);
276
0
    return ret;
277
0
 error:
278
0
    if (pathv != NULL)
279
0
        for (int i=0; i < pathc; i++)
280
0
            free(pathv[i]);
281
0
    free(pathv);
282
0
    ret = -1;
283
0
    goto done;
284
0
}
285
286
0
int filter_matches(struct tree *xfm, const char *path) {
287
0
    int found = 0;
288
0
    list_for_each(f, xfm->children) {
289
0
        if (is_incl(f) && fnmatch_normalize(f->value, path, fnm_flags) == 0) {
290
0
            found = 1;
291
0
            break;
292
0
        }
293
0
    }
294
0
    if (! found)
295
0
        return 0;
296
0
    list_for_each(f, xfm->children) {
297
0
        if (is_excl(f) && (fnmatch_normalize(f->value, path, fnm_flags) == 0))
298
0
            return 0;
299
0
    }
300
0
    return 1;
301
0
}
302
303
/*
304
 * Transformers
305
 */
306
0
struct transform *make_transform(struct lens *lens, struct filter *filter) {
307
0
    struct transform *xform;
308
0
    make_ref(xform);
309
0
    xform->lens = lens;
310
0
    xform->filter = filter;
311
0
    return xform;
312
0
}
313
314
0
void free_transform(struct transform *xform) {
315
0
    if (xform == NULL)
316
0
        return;
317
0
    assert(xform->ref == 0);
318
0
    unref(xform->lens, lens);
319
0
    unref(xform->filter, filter);
320
0
    free(xform);
321
0
}
322
323
0
static char *err_path(const char *filename) {
324
0
    char *result = NULL;
325
0
    if (filename == NULL)
326
0
        pathjoin(&result, 2, AUGEAS_META_FILES, s_error);
327
0
    else
328
0
        pathjoin(&result, 3, AUGEAS_META_FILES, filename, s_error);
329
0
    return result;
330
0
}
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
0
                            const char *format, ...) {
336
0
    int r;
337
0
    va_list ap;
338
0
    char *value = NULL;
339
0
    struct tree *tree = NULL;
340
341
0
    va_start(ap, format);
342
0
    r = vasprintf(&value, format, ap);
343
0
    va_end(ap);
344
0
    if (r < 0)
345
0
        value = NULL;
346
0
    ERR_NOMEM(r < 0, aug);
347
348
0
    tree = tree_child_cr(err_info, sub);
349
0
    ERR_NOMEM(tree == NULL, aug);
350
351
0
    r = tree_set_value(tree, value);
352
0
    ERR_NOMEM(r < 0, aug);
353
354
0
 error:
355
0
    free(value);
356
0
    return tree;
357
0
}
358
359
static struct tree *err_lens_entry(struct augeas *aug, struct tree *where,
360
0
                           struct lens *lens, const char *label) {
361
0
    struct tree *result = NULL;
362
363
0
    if (lens == NULL)
364
0
        return NULL;
365
366
0
    char *fi = format_info(lens->info);
367
0
    if (fi != NULL) {
368
0
        result = err_set(aug, where, label, "%s", fi);
369
0
        free(fi);
370
0
    }
371
0
    return result;
372
0
}
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
0
                       const struct lns_error *err, const char *text) {
384
0
    struct tree *err_info = NULL, *finfo = NULL;
385
0
    char *fip = NULL;
386
0
    int r;
387
0
    int result = -1;
388
389
0
    if (filename != NULL) {
390
0
        r = pathjoin(&fip, 2, AUGEAS_META_FILES, filename);
391
0
    } else {
392
0
        r = pathjoin(&fip, 2, AUGEAS_META_TEXT, path);
393
0
    }
394
0
    ERR_NOMEM(r < 0, aug);
395
396
0
    finfo = tree_fpath_cr(aug, fip);
397
0
    ERR_BAIL(aug);
398
399
0
    if (status != NULL) {
400
0
        err_info = tree_child_cr(finfo, s_error);
401
0
        ERR_NOMEM(err_info == NULL, aug);
402
403
0
        r = tree_set_value(err_info, status);
404
0
        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
0
        if (err != NULL) {
409
0
            if (err->pos >= 0) {
410
0
                size_t line, ofs;
411
0
                err_set(aug, err_info, s_pos, "%d", err->pos);
412
0
                if (text != NULL) {
413
0
                    calc_line_ofs(text, err->pos, &line, &ofs);
414
0
                    err_set(aug, err_info, s_line, "%zd", line);
415
0
                    err_set(aug, err_info, s_char, "%zd", ofs);
416
0
                }
417
0
            }
418
0
            if (err->path != NULL) {
419
0
                err_set(aug, err_info, s_path, "%s%s", path, err->path);
420
0
            }
421
0
            struct tree *t = err_lens_entry(aug, err_info, err->lens, s_lens);
422
0
            if (t != NULL) {
423
0
                err_lens_entry(aug, t, err->last, s_last);
424
0
                err_lens_entry(aug, t, err->next, s_next);
425
0
            }
426
0
            err_set(aug, err_info, s_message, "%s", err->message);
427
0
        } else if (errnum != 0) {
428
0
            const char *msg = strerror(errnum);
429
0
            err_set(aug, err_info, s_message, "%s", msg);
430
0
        }
431
0
    } else {
432
        /* No error, nuke the error node if it exists */
433
0
        err_info = tree_child(finfo, s_error);
434
0
        if (err_info != NULL)
435
0
            tree_unlink(aug, err_info);
436
0
    }
437
438
0
    tree_clean(finfo);
439
0
    result = 0;
440
0
 error:
441
0
    free(fip);
442
0
    return result;
443
0
}
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
0
                         const char *filename, bool force_reload) {
456
0
    struct tree *file, *tree;
457
0
    char *tmp = NULL;
458
0
    int r;
459
0
    char *path = NULL;
460
0
    int result = -1;
461
462
0
    if (lens == NULL)
463
0
        return -1;
464
465
0
    r = pathjoin(&path, 2, AUGEAS_META_TREE, node);
466
0
    ERR_NOMEM(r < 0, aug);
467
468
0
    file = tree_fpath_cr(aug, path);
469
0
    file->file = true;
470
0
    ERR_BAIL(aug);
471
472
    /* Set 'path' */
473
0
    tree = tree_child_cr(file, s_path);
474
0
    ERR_NOMEM(tree == NULL, aug);
475
0
    r = tree_set_value(tree, node);
476
0
    ERR_NOMEM(r < 0, aug);
477
478
    /* Set 'mtime' */
479
0
    if (force_reload) {
480
0
        tmp = strdup("0");
481
0
        ERR_NOMEM(tmp == NULL, aug);
482
0
    } else {
483
0
        tmp = mtime_as_string(aug, filename);
484
0
        ERR_BAIL(aug);
485
0
    }
486
0
    tree = tree_child_cr(file, s_mtime);
487
0
    ERR_NOMEM(tree == NULL, aug);
488
0
    tree_store_value(tree, &tmp);
489
490
    /* Set 'lens/info' */
491
0
    tmp = format_info(lens->info);
492
0
    ERR_NOMEM(tmp == NULL, aug);
493
0
    tree = tree_path_cr(file, 2, s_lens, s_info);
494
0
    ERR_NOMEM(tree == NULL, aug);
495
0
    r = tree_set_value(tree, tmp);
496
0
    ERR_NOMEM(r < 0, aug);
497
0
    FREE(tmp);
498
499
    /* Set 'lens' */
500
0
    tree = tree->parent;
501
0
    r = tree_set_value(tree, lens_name);
502
0
    ERR_NOMEM(r < 0, aug);
503
504
0
    tree_clean(file);
505
506
0
    result = 0;
507
0
 error:
508
0
    free(path);
509
0
    free(tmp);
510
0
    return result;
511
0
}
512
513
0
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
0
    if (len == 0 || text[len-1] != '\n') {
518
0
        if (REALLOC_N(text, len+2) == 0) {
519
0
            text[len] = '\n';
520
0
            text[len+1] = '\0';
521
0
        }
522
0
    }
523
0
    return text;
524
0
}
525
526
/* Turn the file name FNAME, which starts with aug->root, into
527
 * a path in the tree underneath /files */
528
0
static char *file_name_path(struct augeas *aug, const char *fname) {
529
0
    char *path = NULL;
530
531
0
    pathjoin(&path, 2, AUGEAS_FILES_TREE, fname + strlen(aug->root) - 1);
532
0
    return path;
533
0
}
534
535
/* Replace the subtree for FPATH with SUB */
536
static void tree_freplace(struct augeas *aug, const char *fpath,
537
0
                         struct tree *sub) {
538
0
    struct tree *parent;
539
540
0
    parent = tree_fpath_cr(aug, fpath);
541
0
    ERR_RET(aug);
542
543
0
    parent->file = true;
544
0
    tree_unlink_children(aug, parent);
545
0
    list_append(parent->children, sub);
546
0
    list_for_each(s, sub) {
547
0
        s->parent = parent;
548
0
    }
549
0
}
550
551
static struct info*
552
make_lns_info(struct augeas *aug, const char *filename,
553
0
              const char *text, int text_len) {
554
0
    struct info *info = NULL;
555
556
0
    make_ref(info);
557
0
    ERR_NOMEM(info == NULL, aug);
558
559
0
    if (filename != NULL) {
560
0
        make_ref(info->filename);
561
0
        ERR_NOMEM(info->filename == NULL, aug);
562
0
        info->filename->str = strdup(filename);
563
0
    }
564
565
0
    info->first_line = 1;
566
0
    info->last_line = 1;
567
0
    info->first_column = 1;
568
0
    if (text != NULL) {
569
0
        info->last_column = text_len;
570
0
    }
571
572
0
    info->error = aug->error;
573
574
0
    return info;
575
0
 error:
576
0
    unref(info, info);
577
0
    return NULL;
578
0
}
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
0
                     struct lns_error **err) {
594
0
    struct info *info = NULL;
595
0
    struct span *span = NULL;
596
0
    struct tree *tree = NULL;
597
598
0
    info = make_lns_info(aug, filename, text, text_len);
599
0
    ERR_BAIL(aug);
600
601
0
    if (aug->flags & AUG_ENABLE_SPAN) {
602
        /* Allocate the span already to capture a reference to
603
           info->filename */
604
0
        span = make_span(info);
605
0
        ERR_NOMEM(span == NULL, info);
606
0
    }
607
608
0
    tree = lns_get(info, lens, text, aug->flags & AUG_ENABLE_SPAN, err);
609
610
0
    if (*err == NULL) {
611
        // Successful get
612
0
        tree_freplace(aug, path, tree);
613
0
        ERR_BAIL(aug);
614
615
        /* top level node span entire file length */
616
0
        if (span != NULL && tree != NULL) {
617
0
            tree->parent->span = move(span);
618
0
            tree->parent->span->span_start = 0;
619
0
            tree->parent->span->span_end = text_len;
620
0
        }
621
0
        tree = NULL;
622
0
    }
623
0
 error:
624
0
    free_span(span);
625
0
    unref(info, info);
626
0
    free_tree(tree);
627
0
}
628
629
static int load_file(struct augeas *aug, struct lens *lens,
630
0
                     const char *lens_name, char *filename) {
631
0
    char *text = NULL;
632
0
    const char *err_status = NULL;
633
0
    char *path = NULL;
634
0
    struct lns_error *err = NULL;
635
0
    int result = -1, r, text_len = 0;
636
637
0
    path = file_name_path(aug, filename);
638
0
    ERR_NOMEM(path == NULL, aug);
639
640
0
    r = add_file_info(aug, path, lens, lens_name, filename, false);
641
0
    if (r < 0)
642
0
        goto done;
643
644
0
    text = xread_file(filename);
645
0
    if (text == NULL) {
646
0
        err_status = "read_failed";
647
0
        goto done;
648
0
    }
649
0
    text_len = strlen(text);
650
0
    text = append_newline(text, text_len);
651
652
0
    lens_get(aug, lens, filename, text, text_len, path, &err);
653
0
    if (err != NULL) {
654
0
        err_status = "parse_failed";
655
0
        goto done;
656
0
    }
657
0
    ERR_BAIL(aug);
658
659
0
    result = 0;
660
0
 done:
661
0
    store_error(aug, filename + strlen(aug->root) - 1, path, err_status,
662
0
                errno, err, text);
663
0
 error:
664
0
    free_lns_error(err);
665
0
    free(path);
666
0
    free(text);
667
0
    return result;
668
0
}
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
0
static struct lens *lens_from_name(struct augeas *aug, const char *name) {
676
0
    struct lens *result = NULL;
677
678
0
    if (name[0] == '@') {
679
0
        struct module *modl = NULL;
680
0
        for (modl = aug->modules;
681
0
             modl != NULL && !streqv(modl->name, name + 1);
682
0
             modl = modl->next);
683
0
        ERR_THROW(modl == NULL, aug, AUG_ENOLENS,
684
0
                  "Could not find module %s", name + 1);
685
0
        ERR_THROW(modl->autoload == NULL, aug, AUG_ENOLENS,
686
0
                  "No autoloaded lens in module %s", name + 1);
687
0
        result = modl->autoload->lens;
688
0
    } else {
689
0
        result = lens_lookup(aug, name);
690
0
    }
691
0
    ERR_THROW(result == NULL, aug, AUG_ENOLENS,
692
0
              "Can not find lens %s", name);
693
0
    return result;
694
0
 error:
695
0
    return NULL;
696
0
}
697
698
int text_store(struct augeas *aug, const char *lens_path,
699
0
               const char *path, const char *text) {
700
0
    struct lns_error *err = NULL;
701
0
    int result = -1;
702
0
    const char *err_status = NULL;
703
0
    struct lens *lens = NULL;
704
705
0
    lens = lens_from_name(aug, lens_path);
706
0
    ERR_BAIL(aug);
707
708
0
    lens_get(aug, lens, path, text, strlen(text), path, &err);
709
0
    if (err != NULL) {
710
0
        err_status = "parse_failed";
711
0
        goto error;
712
0
    }
713
0
    ERR_BAIL(aug);
714
715
0
    result = 0;
716
0
 error:
717
0
    store_error(aug, NULL, path, err_status, errno, err, text);
718
0
    free_lns_error(err);
719
0
    return result;
720
0
}
721
722
0
const char *xfm_lens_name(struct tree *xfm) {
723
0
    struct tree *l = tree_child(xfm, s_lens);
724
725
0
    if (l == NULL)
726
0
        return "(unknown)";
727
0
    if (l->value == NULL)
728
0
        return "(noname)";
729
0
    return l->value;
730
0
}
731
732
struct lens *xfm_lens(struct augeas *aug,
733
0
                      struct tree *xfm, const char **lens_name) {
734
0
    struct tree *l = NULL;
735
736
0
    if (lens_name != NULL)
737
0
        *lens_name = NULL;
738
739
0
    for (l = xfm->children;
740
0
         l != NULL && !streqv("lens", l->label);
741
0
         l = l->next);
742
743
0
    if (l == NULL || l->value == NULL)
744
0
        return NULL;
745
0
    if (lens_name != NULL)
746
0
        *lens_name = l->value;
747
748
0
    return lens_from_name(aug, l->value);
749
0
}
750
751
0
static void xfm_error(struct tree *xfm, const char *msg) {
752
0
    char *v = msg ? strdup(msg) : NULL;
753
0
    char *l = strdup("error");
754
755
0
    if (l == NULL || v == NULL) {
756
0
        free(v);
757
0
        free(l);
758
0
        return;
759
0
    }
760
0
    tree_append(xfm, l, v);
761
0
}
762
763
0
int transform_validate(struct augeas *aug, struct tree *xfm) {
764
0
    struct tree *l = NULL;
765
766
0
    for (struct tree *t = xfm->children; t != NULL; ) {
767
0
        if (streqv(t->label, "lens")) {
768
0
            l = t;
769
0
        } else if ((is_incl(t) || (is_excl(t) && strchr(t->value, SEP) != NULL))
770
0
                       && t->value[0] != SEP) {
771
            /* Normalize relative paths to absolute ones */
772
0
            int r;
773
0
            r = REALLOC_N(t->value, strlen(t->value) + 2);
774
0
            ERR_NOMEM(r < 0, aug);
775
0
            memmove(t->value + 1, t->value, strlen(t->value) + 1);
776
0
            t->value[0] = SEP;
777
0
        }
778
779
0
        if (streqv(t->label, "error")) {
780
0
            struct tree *del = t;
781
0
            t = del->next;
782
0
            tree_unlink(aug, del);
783
0
        } else {
784
0
            t = t->next;
785
0
        }
786
0
    }
787
788
0
    if (l == NULL) {
789
0
        xfm_error(xfm, "missing a child with label 'lens'");
790
0
        return -1;
791
0
    }
792
0
    if (l->value == NULL) {
793
0
        xfm_error(xfm, "the 'lens' node does not contain a lens name");
794
0
        return -1;
795
0
    }
796
0
    lens_from_name(aug, l->value);
797
0
    ERR_BAIL(aug);
798
799
0
    return 0;
800
0
 error:
801
0
    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
0
    reset_error(aug->error);
805
0
    return -1;
806
0
}
807
808
void transform_file_error(struct augeas *aug, const char *status,
809
0
                          const char *filename, const char *format, ...) {
810
0
    char *ep = err_path(filename);
811
0
    struct tree *err;
812
0
    char *msg;
813
0
    va_list ap;
814
0
    int r;
815
816
0
    err = tree_fpath_cr(aug, ep);
817
0
    FREE(ep);
818
0
    if (err == NULL)
819
0
        return;
820
821
0
    tree_unlink_children(aug, err);
822
0
    tree_set_value(err, status);
823
824
0
    err = tree_child_cr(err, s_message);
825
0
    if (err == NULL)
826
0
        return;
827
828
0
    va_start(ap, format);
829
0
    r = vasprintf(&msg, format, ap);
830
0
    va_end(ap);
831
0
    if (r < 0)
832
0
        return;
833
0
    tree_set_value(err, msg);
834
0
    free(msg);
835
0
}
836
837
0
static struct tree *file_info(struct augeas *aug, const char *fname) {
838
0
    char *path = NULL;
839
0
    struct tree *result = NULL;
840
0
    int r;
841
842
0
    r = pathjoin(&path, 2, AUGEAS_META_FILES, fname);
843
0
    ERR_NOMEM(r < 0, aug);
844
845
0
    result = tree_fpath(aug, path);
846
0
    ERR_BAIL(aug);
847
0
 error:
848
0
    free(path);
849
0
    return result;
850
0
}
851
852
0
int transform_load(struct augeas *aug, struct tree *xfm, const char *file) {
853
0
    int nmatches = 0;
854
0
    char **matches;
855
0
    const char *lens_name;
856
0
    struct lens *lens = xfm_lens(aug, xfm, &lens_name);
857
0
    int r;
858
859
0
    if (lens == NULL) {
860
        // FIXME: Record an error and return 0
861
0
        return -1;
862
0
    }
863
864
0
    r = filter_generate(xfm, aug->root, &nmatches, &matches);
865
0
    if (r == -1)
866
0
        return -1;
867
0
    for (int i=0; i < nmatches; i++) {
868
0
        const char *filename = matches[i] + strlen(aug->root) - 1;
869
0
        struct tree *finfo = file_info(aug, filename);
870
871
0
        if (file != NULL && STRNEQ(filename, file)) {
872
0
            FREE(matches[i]);
873
0
            continue;
874
0
        }
875
876
0
        if (finfo != NULL && !finfo->dirty &&
877
0
            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
0
            const char *s = xfm_lens_name(finfo);
888
0
            struct lens *other_lens = lens_from_name(aug, s);
889
0
            if (lens != other_lens) {
890
0
                char *fpath = file_name_path(aug, matches[i]);
891
0
                transform_file_error(aug, "mxfm_load", filename,
892
0
                  "Lenses %s and %s could be used to load this file",
893
0
                                     s, lens_name);
894
0
                aug_rm(aug, fpath);
895
0
                free(fpath);
896
0
            }
897
0
        } else if (!file_current(aug, matches[i], finfo)) {
898
0
            load_file(aug, lens, lens_name, matches[i]);
899
0
        }
900
0
        if (finfo != NULL)
901
0
            finfo->dirty = 0;
902
0
        FREE(matches[i]);
903
0
    }
904
0
    lens_release(lens);
905
0
    free(matches);
906
0
    return 0;
907
0
}
908
909
0
int transform_applies(struct tree *xfm, const char *path) {
910
0
    if (STRNEQLEN(path, AUGEAS_FILES_TREE, strlen(AUGEAS_FILES_TREE))
911
0
        || path[strlen(AUGEAS_FILES_TREE)] != SEP)
912
0
        return 0;
913
0
    return filter_matches(xfm, path + strlen(AUGEAS_FILES_TREE));
914
0
}
915
916
static int transfer_file_attrs(FILE *from, FILE *to,
917
0
                               const char **err_status) {
918
0
    struct stat st;
919
0
    int ret = 0;
920
0
    int selinux_enabled = (is_selinux_enabled() > 0);
921
0
    char *con = NULL;
922
923
0
    int from_fd;
924
0
    int to_fd = fileno(to);
925
926
0
    if (from == NULL) {
927
0
        *err_status = "replace_from_missing";
928
0
        return -1;
929
0
    }
930
931
0
    from_fd = fileno(from);
932
933
0
    ret = fstat(from_fd, &st);
934
0
    if (ret < 0) {
935
0
        *err_status = "replace_stat";
936
0
        return -1;
937
0
    }
938
0
    if (selinux_enabled) {
939
0
        if (fgetfilecon(from_fd, &con) < 0 && errno != ENOTSUP) {
940
0
            *err_status = "replace_getfilecon";
941
0
            return -1;
942
0
        }
943
0
    }
944
945
0
    if (fchown(to_fd, st.st_uid, st.st_gid) < 0) {
946
0
        *err_status = "replace_chown";
947
0
        return -1;
948
0
    }
949
0
    if (fchmod(to_fd, st.st_mode) < 0) {
950
0
        *err_status = "replace_chmod";
951
0
        return -1;
952
0
    }
953
0
    if (selinux_enabled && con != NULL) {
954
0
        if (fsetfilecon(to_fd, con) < 0 && errno != ENOTSUP) {
955
0
            *err_status = "replace_setfilecon";
956
0
            return -1;
957
0
        }
958
0
        freecon(con);
959
0
    }
960
0
    return 0;
961
0
}
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
0
                      int unlink_if_rename_fails) {
979
0
    FILE *from_fp = NULL, *to_fp = NULL;
980
0
    char buf[BUFSIZ];
981
0
    size_t len;
982
0
    int to_fd = -1, to_oflags, r;
983
0
    int result = -1;
984
985
0
    if (rename(from, to) == 0)
986
0
        return 0;
987
0
    if ((errno != EXDEV && errno != EBUSY) || !copy_if_rename_fails) {
988
0
        *err_status = "rename";
989
0
        return -1;
990
0
    }
991
992
    /* rename not possible, copy file contents */
993
0
    if (!(from_fp = fopen(from, "r"))) {
994
0
        *err_status = "clone_open_src";
995
0
        goto done;
996
0
    }
997
998
0
    if (unlink_if_rename_fails) {
999
0
        r = unlink(to);
1000
0
        if (r < 0) {
1001
0
            *err_status = "clone_unlink_dst";
1002
0
            goto done;
1003
0
        }
1004
0
    }
1005
1006
0
    to_oflags = unlink_if_rename_fails ? O_EXCL : O_TRUNC;
1007
0
    if ((to_fd = open(to, O_WRONLY|O_CREAT|to_oflags, S_IRUSR|S_IWUSR)) < 0) {
1008
0
        *err_status = "clone_open_dst";
1009
0
        goto done;
1010
0
    }
1011
0
    if (!(to_fp = fdopen(to_fd, "w"))) {
1012
0
        *err_status = "clone_fdopen_dst";
1013
0
        goto done;
1014
0
    }
1015
1016
0
    if (transfer_file_attrs(from_fp, to_fp, err_status) < 0)
1017
0
        goto done;
1018
1019
0
    while ((len = fread(buf, 1, BUFSIZ, from_fp)) > 0) {
1020
0
        if (fwrite(buf, 1, len, to_fp) != len) {
1021
0
            *err_status = "clone_write";
1022
0
            goto done;
1023
0
        }
1024
0
    }
1025
0
    if (ferror(from_fp)) {
1026
0
        *err_status = "clone_read";
1027
0
        goto done;
1028
0
    }
1029
0
    if (fflush(to_fp) != 0) {
1030
0
        *err_status = "clone_flush";
1031
0
        goto done;
1032
0
    }
1033
0
    if (fsync(fileno(to_fp)) < 0) {
1034
0
        *err_status = "clone_sync";
1035
0
        goto done;
1036
0
    }
1037
0
    result = 0;
1038
0
 done:
1039
0
    if (from_fp != NULL)
1040
0
        fclose(from_fp);
1041
0
    if (to_fp != NULL) {
1042
0
        if (fclose(to_fp) != 0) {
1043
0
            *err_status = "clone_fclose_dst";
1044
0
            result = -1;
1045
0
        }
1046
0
    } else if (to_fd >= 0 && close(to_fd) < 0) {
1047
0
        *err_status = "clone_close_dst";
1048
0
        result = -1;
1049
0
    }
1050
0
    if (result != 0)
1051
0
        unlink(to);
1052
0
    if (result == 0)
1053
0
        unlink(from);
1054
0
    return result;
1055
0
}
1056
1057
0
static char *strappend(const char *s1, const char *s2) {
1058
0
    size_t len = strlen(s1) + strlen(s2);
1059
0
    char *result = NULL, *p;
1060
1061
0
    if (ALLOC_N(result, len + 1) < 0)
1062
0
        return NULL;
1063
1064
0
    p = stpcpy(result, s1);
1065
0
    stpcpy(p, s2);
1066
0
    return result;
1067
0
}
1068
1069
0
static int file_saved_event(struct augeas *aug, const char *path) {
1070
0
    const char *saved = strrchr(AUGEAS_EVENTS_SAVED, SEP) + 1;
1071
0
    struct pathx *px;
1072
0
    struct tree *dummy;
1073
0
    int r;
1074
1075
0
    px = pathx_aug_parse(aug, aug->origin, NULL,
1076
0
                         AUGEAS_EVENTS_SAVED "[last()]", true);
1077
0
    ERR_BAIL(aug);
1078
1079
0
    if (pathx_find_one(px, &dummy) == 1) {
1080
0
        r = tree_insert(px, saved, 0);
1081
0
        if (r < 0)
1082
0
            goto error;
1083
0
    }
1084
1085
0
    if (! tree_set(px, path))
1086
0
        goto error;
1087
1088
0
    free_pathx(px);
1089
0
    return 0;
1090
0
 error:
1091
0
    free_pathx(px);
1092
0
    return -1;
1093
0
}
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
0
                     FILE *out, struct lns_error **err) {
1102
0
    struct info *info = NULL;
1103
0
    size_t text_len = strlen(text);
1104
0
    bool with_span = aug->flags & AUG_ENABLE_SPAN;
1105
1106
0
    info = make_lns_info(aug, filename, text, text_len);
1107
0
    ERR_BAIL(aug);
1108
1109
0
    if (with_span) {
1110
0
        if (tree->span == NULL) {
1111
0
            tree->span = make_span(info);
1112
0
            ERR_NOMEM(tree->span == NULL, aug);
1113
0
        }
1114
0
        tree->span->span_start = ftell(out);
1115
0
    }
1116
1117
0
    lns_put(info, out, lens, tree->children, text,
1118
0
            aug->flags & AUG_ENABLE_SPAN, err);
1119
1120
0
    if (with_span) {
1121
0
        tree->span->span_end = ftell(out);
1122
0
    }
1123
0
 error:
1124
0
    unref(info, info);
1125
0
}
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
0
                   const char *path, struct tree *tree) {
1160
0
    int   fd;
1161
0
    FILE *fp = NULL, *augorig_canon_fp = NULL;
1162
0
    char *augtemp = NULL, *augnew = NULL, *augorig = NULL, *augsave = NULL;
1163
0
    char *augorig_canon = NULL, *augdest = NULL;
1164
0
    int   augorig_exists;
1165
0
    int   copy_if_rename_fails = 0;
1166
0
    char *text = NULL;
1167
0
    const char *filename = path + strlen(AUGEAS_FILES_TREE) + 1;
1168
0
    const char *err_status = NULL;
1169
0
    char *dyn_err_status = NULL;
1170
0
    struct lns_error *err = NULL;
1171
0
    const char *lens_name;
1172
0
    struct lens *lens = xfm_lens(aug, xfm, &lens_name);
1173
0
    int result = -1, r;
1174
0
    bool force_reload;
1175
0
    struct info *info = NULL;
1176
1177
0
    errno = 0;
1178
1179
0
    if (lens == NULL) {
1180
0
        err_status = "lens_name";
1181
0
        goto done;
1182
0
    }
1183
1184
0
    copy_if_rename_fails =
1185
0
        aug_get(aug, AUGEAS_COPY_IF_RENAME_FAILS, NULL) == 1;
1186
1187
0
    if (asprintf(&augorig, "%s%s", aug->root, filename) == -1) {
1188
0
        augorig = NULL;
1189
0
        goto done;
1190
0
    }
1191
1192
0
    augorig_canon = canonicalize_file_name(augorig);
1193
0
    augorig_exists = 1;
1194
0
    if (augorig_canon == NULL) {
1195
0
        if (errno == ENOENT) {
1196
0
            augorig_canon = augorig;
1197
0
            augorig_exists = 0;
1198
0
        } else {
1199
0
            err_status = "canon_augorig";
1200
0
            goto done;
1201
0
        }
1202
0
    }
1203
1204
0
    if (access(augorig_canon, R_OK) == 0) {
1205
0
        augorig_canon_fp = fopen(augorig_canon, "r");
1206
0
        text = xfread_file(augorig_canon_fp);
1207
0
    } else {
1208
0
        text = strdup("");
1209
0
    }
1210
1211
0
    if (text == NULL) {
1212
0
        err_status = "put_read";
1213
0
        goto done;
1214
0
    }
1215
1216
0
    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
0
    if (aug->flags & AUG_SAVE_NEWFILE) {
1221
0
        if (xasprintf(&augnew, "%s" EXT_AUGNEW, augorig) < 0) {
1222
0
            err_status = "augnew_oom";
1223
0
            goto done;
1224
0
        }
1225
0
        augdest = augnew;
1226
0
    } else {
1227
0
        augdest = augorig_canon;
1228
0
    }
1229
1230
0
    if (xasprintf(&augtemp, "%s.XXXXXX", augdest) < 0) {
1231
0
        err_status = "augtemp_oom";
1232
0
        goto done;
1233
0
    }
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
0
    fd = mkstemp(augtemp);
1239
0
    if (fd < 0) {
1240
0
        err_status = "mk_augtemp";
1241
0
        goto done;
1242
0
    }
1243
0
    fp = fdopen(fd, "w");
1244
0
    if (fp == NULL) {
1245
0
        err_status = "open_augtemp";
1246
0
        goto done;
1247
0
    }
1248
1249
0
    if (augorig_exists) {
1250
0
        if (transfer_file_attrs(augorig_canon_fp, fp, &err_status) != 0) {
1251
0
            goto done;
1252
0
        }
1253
0
    } 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
0
        mode_t curumsk = umask(022);
1257
0
        umask(curumsk);
1258
1259
0
        if (fchmod(fileno(fp), 0666 & ~curumsk) < 0) {
1260
0
            err_status = "create_chmod";
1261
0
            goto done;
1262
0
        }
1263
0
    }
1264
1265
0
    if (tree != NULL) {
1266
0
        lens_put(aug, augorig_canon, lens, text, tree, fp, &err);
1267
0
        ERR_BAIL(aug);
1268
0
    }
1269
1270
0
    if (ferror(fp)) {
1271
0
        err_status = "error_augtemp";
1272
0
        goto done;
1273
0
    }
1274
1275
0
    if (fflush(fp) != 0) {
1276
0
        err_status = "flush_augtemp";
1277
0
        goto done;
1278
0
    }
1279
1280
0
    if (fsync(fileno(fp)) < 0) {
1281
0
        err_status = "sync_augtemp";
1282
0
        goto done;
1283
0
    }
1284
1285
0
    if (fclose(fp) != 0) {
1286
0
        err_status = "close_augtemp";
1287
0
        fp = NULL;
1288
0
        goto done;
1289
0
    }
1290
1291
0
    fp = NULL;
1292
1293
0
    if (err != NULL) {
1294
0
        err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
1295
0
        unlink(augtemp);
1296
0
        goto done;
1297
0
    }
1298
1299
0
    {
1300
0
        char *new_text = xread_file(augtemp);
1301
0
        int same = 0;
1302
0
        if (new_text == NULL) {
1303
0
            err_status = "read_augtemp";
1304
0
            goto done;
1305
0
        }
1306
0
        same = STREQ(text, new_text);
1307
0
        FREE(new_text);
1308
0
        if (same) {
1309
0
            result = 0;
1310
0
            unlink(augtemp);
1311
0
            goto done;
1312
0
        } else if (aug->flags & AUG_SAVE_NOOP) {
1313
0
            result = 1;
1314
0
            unlink(augtemp);
1315
0
            goto done;
1316
0
        }
1317
0
    }
1318
1319
0
    if (!(aug->flags & AUG_SAVE_NEWFILE)) {
1320
0
        if (augorig_exists && (aug->flags & AUG_SAVE_BACKUP)) {
1321
0
            r = xasprintf(&augsave, "%s" EXT_AUGSAVE, augorig);
1322
0
            if (r == -1) {
1323
0
                augsave = NULL;
1324
0
                goto done;
1325
0
            }
1326
1327
0
            r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
1328
0
            if (r != 0) {
1329
0
                dyn_err_status = strappend(err_status, "_augsave");
1330
0
                goto done;
1331
0
            }
1332
0
        }
1333
0
    }
1334
1335
0
    r = clone_file(augtemp, augdest, &err_status, copy_if_rename_fails, 0);
1336
0
    if (r != 0) {
1337
0
        unlink(augtemp);
1338
0
        dyn_err_status = strappend(err_status, "_augtemp");
1339
0
        goto done;
1340
0
    }
1341
1342
0
    result = 1;
1343
1344
0
 done:
1345
0
    force_reload = aug->flags & AUG_SAVE_NEWFILE;
1346
0
    r = add_file_info(aug, path, lens, lens_name, augorig, force_reload);
1347
0
    if (r < 0) {
1348
0
        err_status = "file_info";
1349
0
        result = -1;
1350
0
    }
1351
0
    if (result > 0) {
1352
0
        r = file_saved_event(aug, path);
1353
0
        if (r < 0) {
1354
0
            err_status = "saved_event";
1355
0
            result = -1;
1356
0
        }
1357
0
    }
1358
0
    {
1359
0
        const char *emsg =
1360
0
            dyn_err_status == NULL ? err_status : dyn_err_status;
1361
0
        store_error(aug, filename, path, emsg, errno, err, text);
1362
0
    }
1363
0
 error:
1364
0
    free(dyn_err_status);
1365
0
    lens_release(lens);
1366
0
    free(text);
1367
0
    free(augtemp);
1368
0
    free(augnew);
1369
0
    if (augorig_canon != augorig)
1370
0
        free(augorig_canon);
1371
0
    free(augorig);
1372
0
    free(augsave);
1373
0
    free_lns_error(err);
1374
0
    unref(info, info);
1375
1376
0
    if (fp != NULL)
1377
0
        fclose(fp);
1378
0
    if (augorig_canon_fp != NULL)
1379
0
        fclose(augorig_canon_fp);
1380
0
    return result;
1381
0
}
1382
1383
int text_retrieve(struct augeas *aug, const char *lens_name,
1384
                  const char *path, struct tree *tree,
1385
0
                  const char *text_in, char **text_out) {
1386
0
    struct memstream ms;
1387
0
    bool ms_open = false;
1388
0
    const char *err_status = NULL;
1389
0
    struct lns_error *err = NULL;
1390
0
    struct lens *lens = NULL;
1391
0
    int result = -1, r;
1392
0
    struct info *info = NULL;
1393
1394
0
    MEMZERO(&ms, 1);
1395
0
    errno = 0;
1396
1397
0
    lens = lens_from_name(aug, lens_name);
1398
0
    if (lens == NULL) {
1399
0
        err_status = "lens_name";
1400
0
        goto done;
1401
0
    }
1402
1403
0
    r = init_memstream(&ms);
1404
0
    if (r < 0) {
1405
0
        err_status = "init_memstream";
1406
0
        goto done;
1407
0
    }
1408
0
    ms_open = true;
1409
1410
0
    if (tree != NULL) {
1411
0
        lens_put(aug, path, lens, text_in, tree, ms.stream, &err);
1412
0
        ERR_BAIL(aug);
1413
0
    }
1414
1415
0
    r = close_memstream(&ms);
1416
0
    ms_open = false;
1417
0
    if (r < 0) {
1418
0
        err_status = "close_memstream";
1419
0
        goto done;
1420
0
    }
1421
1422
0
    *text_out = ms.buf;
1423
0
    ms.buf = NULL;
1424
1425
0
    if (err != NULL) {
1426
0
        err_status = err->pos >= 0 ? "parse_skel_failed" : "put_failed";
1427
0
        goto done;
1428
0
    }
1429
1430
0
    result = 0;
1431
1432
0
 done:
1433
0
    store_error(aug, NULL, path, err_status, errno, err, text_in);
1434
0
 error:
1435
0
    lens_release(lens);
1436
0
    if (result < 0) {
1437
0
        free(*text_out);
1438
0
        *text_out = NULL;
1439
0
    }
1440
0
    free_lns_error(err);
1441
0
    unref(info, info);
1442
1443
0
    if (ms_open)
1444
0
        close_memstream(&ms);
1445
0
    return result;
1446
0
}
1447
1448
0
int remove_file(struct augeas *aug, struct tree *tree) {
1449
0
    const char *err_status = NULL;
1450
0
    char *dyn_err_status = NULL;
1451
0
    char *augsave = NULL, *augorig = NULL, *augorig_canon = NULL;
1452
0
    struct tree *path = NULL;
1453
0
    const char *file_path = NULL;
1454
0
    char *meta_path = NULL;
1455
0
    int r;
1456
1457
0
    path = tree_child(tree, s_path);
1458
0
    if (path == NULL) {
1459
0
        err_status = "no child called 'path' for file entry";
1460
0
        goto error;
1461
0
    }
1462
0
    file_path = path->value + strlen(AUGEAS_FILES_TREE);
1463
0
    path = NULL;
1464
1465
0
    meta_path = path_of_tree(tree);
1466
0
    if (meta_path == NULL) {
1467
0
        err_status = "path_of_tree";
1468
0
        goto error;
1469
0
    }
1470
1471
0
    if ((augorig = strappend(aug->root, file_path)) == NULL) {
1472
0
        err_status = "root_file";
1473
0
        goto error;
1474
0
    }
1475
1476
0
    augorig_canon = canonicalize_file_name(augorig);
1477
0
    if (augorig_canon == NULL) {
1478
0
        if (errno == ENOENT) {
1479
0
            goto done;
1480
0
        } else {
1481
0
            err_status = "canon_augorig";
1482
0
            goto error;
1483
0
        }
1484
0
    }
1485
1486
0
    r = file_saved_event(aug, meta_path + strlen(AUGEAS_META_TREE));
1487
0
    if (r < 0) {
1488
0
        err_status = "saved_event";
1489
0
        goto error;
1490
0
    }
1491
1492
0
    if (aug->flags & AUG_SAVE_NOOP)
1493
0
        goto done;
1494
1495
0
    if (aug->flags & AUG_SAVE_BACKUP) {
1496
        /* Move file to one with extension .augsave */
1497
0
        r = asprintf(&augsave, "%s" EXT_AUGSAVE, augorig_canon);
1498
0
        if (r == -1) {
1499
0
            augsave = NULL;
1500
0
                goto error;
1501
0
        }
1502
1503
0
        r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
1504
0
        if (r != 0) {
1505
0
            dyn_err_status = strappend(err_status, "_augsave");
1506
0
            goto error;
1507
0
        }
1508
0
    } else {
1509
        /* Unlink file */
1510
0
        r = unlink(augorig_canon);
1511
0
        if (r < 0) {
1512
0
            err_status = "unlink_orig";
1513
0
            goto error;
1514
0
        }
1515
0
    }
1516
0
    path = NULL;
1517
0
    tree_unlink(aug, tree);
1518
0
 done:
1519
0
    free(meta_path);
1520
0
    free(augorig);
1521
0
    free(augorig_canon);
1522
0
    free(augsave);
1523
0
    return 0;
1524
0
 error:
1525
0
    {
1526
0
        const char *emsg =
1527
0
            dyn_err_status == NULL ? err_status : dyn_err_status;
1528
0
        store_error(aug, file_path, meta_path, emsg, errno, NULL, NULL);
1529
0
    }
1530
0
    free(meta_path);
1531
0
    free(augorig);
1532
0
    free(augorig_canon);
1533
0
    free(augsave);
1534
0
    free(dyn_err_status);
1535
0
    return -1;
1536
0
}
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
 */