/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 | | */ |