]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/fileio.c
man: document ARM root partition types
[thirdparty/systemd.git] / src / shared / fileio.c
CommitLineData
a5c32cff
HH
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <unistd.h>
e93c33d4 23#include <sys/sendfile.h>
a5c32cff
HH
24#include "fileio.h"
25#include "util.h"
26#include "strv.h"
335c46b5 27#include "utf8.h"
1e5413f7 28#include "ctype.h"
a5c32cff 29
b4bc041b 30int write_string_to_file(FILE *f, const char *line) {
a5c32cff 31 errno = 0;
fec6fc6b 32 fputs(line, f);
a5c32cff
HH
33 if (!endswith(line, "\n"))
34 fputc('\n', f);
35
36 fflush(f);
37
38 if (ferror(f))
39 return errno ? -errno : -EIO;
40
41 return 0;
42}
43
b4bc041b
ZJS
44int write_string_file(const char *fn, const char *line) {
45 _cleanup_fclose_ FILE *f = NULL;
46
47 assert(fn);
48 assert(line);
49
50 f = fopen(fn, "we");
51 if (!f)
52 return -errno;
53
54 return write_string_to_file(f, line);
55}
56
574d5f2d 57int write_string_file_atomic(const char *fn, const char *line) {
a5c32cff
HH
58 _cleanup_fclose_ FILE *f = NULL;
59 _cleanup_free_ char *p = NULL;
60 int r;
61
62 assert(fn);
63 assert(line);
64
65 r = fopen_temporary(fn, &f, &p);
66 if (r < 0)
67 return r;
68
69 fchmod_umask(fileno(f), 0644);
70
71 errno = 0;
fec6fc6b 72 fputs(line, f);
a5c32cff
HH
73 if (!endswith(line, "\n"))
74 fputc('\n', f);
75
76 fflush(f);
77
78 if (ferror(f))
79 r = errno ? -errno : -EIO;
80 else {
81 if (rename(p, fn) < 0)
82 r = -errno;
83 else
84 r = 0;
85 }
86
a5c32cff
HH
87 if (r < 0)
88 unlink(p);
89
90 return r;
91}
92
93int read_one_line_file(const char *fn, char **line) {
94 _cleanup_fclose_ FILE *f = NULL;
95 char t[LINE_MAX], *c;
96
97 assert(fn);
98 assert(line);
99
100 f = fopen(fn, "re");
101 if (!f)
102 return -errno;
103
104 if (!fgets(t, sizeof(t), f)) {
105
106 if (ferror(f))
107 return errno ? -errno : -EIO;
108
109 t[0] = 0;
110 }
111
112 c = strdup(t);
113 if (!c)
114 return -ENOMEM;
115 truncate_nl(c);
116
117 *line = c;
118 return 0;
119}
120
e93c33d4
SL
121ssize_t sendfile_full(int out_fd, const char *fn) {
122 _cleanup_fclose_ FILE *f;
123 struct stat st;
124 int r;
125 ssize_t s;
126
127 size_t n, l;
128 _cleanup_free_ char *buf = NULL;
129
130 assert(out_fd > 0);
131 assert(fn);
132
c8a202b7 133 f = fopen(fn, "re");
e93c33d4
SL
134 if (!f)
135 return -errno;
136
137 r = fstat(fileno(f), &st);
138 if (r < 0)
139 return -errno;
140
141 s = sendfile(out_fd, fileno(f), NULL, st.st_size);
142 if (s < 0)
143 if (errno == EINVAL || errno == ENOSYS) {
144 /* continue below */
145 } else
146 return -errno;
147 else
148 return s;
149
150 /* sendfile() failed, fall back to read/write */
151
152 /* Safety check */
153 if (st.st_size > 4*1024*1024)
154 return -E2BIG;
155
156 n = st.st_size > 0 ? st.st_size : LINE_MAX;
157 l = 0;
158
159 while (true) {
160 char *t;
161 size_t k;
162
163 t = realloc(buf, n);
164 if (!t)
165 return -ENOMEM;
166
167 buf = t;
168 k = fread(buf + l, 1, n - l, f);
169
170 if (k <= 0) {
171 if (ferror(f))
172 return -errno;
173
174 break;
175 }
176
177 l += k;
178 n *= 2;
179
180 /* Safety check */
181 if (n > 4*1024*1024)
182 return -E2BIG;
183 }
184
185 r = write(out_fd, buf, l);
186 if (r < 0)
187 return -errno;
188
189 return (ssize_t) l;
190}
191
a5c32cff
HH
192int read_full_file(const char *fn, char **contents, size_t *size) {
193 _cleanup_fclose_ FILE *f = NULL;
194 size_t n, l;
195 _cleanup_free_ char *buf = NULL;
196 struct stat st;
197
198 assert(fn);
199 assert(contents);
200
201 f = fopen(fn, "re");
202 if (!f)
203 return -errno;
204
205 if (fstat(fileno(f), &st) < 0)
206 return -errno;
207
208 /* Safety check */
209 if (st.st_size > 4*1024*1024)
210 return -E2BIG;
211
212 n = st.st_size > 0 ? st.st_size : LINE_MAX;
213 l = 0;
214
215 for (;;) {
216 char *t;
217 size_t k;
218
219 t = realloc(buf, n+1);
220 if (!t)
221 return -ENOMEM;
222
223 buf = t;
224 k = fread(buf + l, 1, n - l, f);
225
226 if (k <= 0) {
227 if (ferror(f))
228 return -errno;
229
230 break;
231 }
232
233 l += k;
234 n *= 2;
235
236 /* Safety check */
237 if (n > 4*1024*1024)
238 return -E2BIG;
239 }
240
241 buf[l] = 0;
242 *contents = buf;
d78a28e3 243 buf = NULL; /* do not free */
a5c32cff
HH
244
245 if (size)
246 *size = l;
247
248 return 0;
249}
250
f73141d7 251static int parse_env_file_internal(
a5c32cff 252 const char *fname,
f73141d7 253 const char *newline,
335c46b5
ZJS
254 int (*push) (const char *filename, unsigned line,
255 const char *key, char *value, void *userdata),
f73141d7
LP
256 void *userdata) {
257
258 _cleanup_free_ char *contents = NULL, *key = NULL;
2b77f67e 259 size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
f73141d7
LP
260 char *p, *value = NULL;
261 int r;
335c46b5 262 unsigned line = 1;
a5c32cff 263
f73141d7
LP
264 enum {
265 PRE_KEY,
266 KEY,
f73141d7
LP
267 PRE_VALUE,
268 VALUE,
269 VALUE_ESCAPE,
270 SINGLE_QUOTE_VALUE,
271 SINGLE_QUOTE_VALUE_ESCAPE,
272 DOUBLE_QUOTE_VALUE,
273 DOUBLE_QUOTE_VALUE_ESCAPE,
274 COMMENT,
275 COMMENT_ESCAPE
276 } state = PRE_KEY;
a5c32cff
HH
277
278 assert(fname);
f73141d7 279 assert(newline);
a5c32cff 280
1c17cbed
ZJS
281 r = read_full_file(fname, &contents, NULL);
282 if (r < 0)
a5c32cff
HH
283 return r;
284
f73141d7
LP
285 for (p = contents; *p; p++) {
286 char c = *p;
287
288 switch (state) {
289
290 case PRE_KEY:
ebc05a09 291 if (strchr(COMMENTS, c))
f73141d7
LP
292 state = COMMENT;
293 else if (!strchr(WHITESPACE, c)) {
294 state = KEY;
2b77f67e
LP
295 last_key_whitespace = (size_t) -1;
296
f73141d7
LP
297 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
298 r = -ENOMEM;
299 goto fail;
300 }
301
302 key[n_key++] = c;
303 }
304 break;
305
306 case KEY:
307 if (strchr(newline, c)) {
308 state = PRE_KEY;
335c46b5 309 line ++;
f73141d7 310 n_key = 0;
2b77f67e 311 } else if (c == '=') {
f73141d7 312 state = PRE_VALUE;
2b77f67e
LP
313 last_value_whitespace = (size_t) -1;
314 } else {
315 if (!strchr(WHITESPACE, c))
316 last_key_whitespace = (size_t) -1;
317 else if (last_key_whitespace == (size_t) -1)
318 last_key_whitespace = n_key;
319
f73141d7
LP
320 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
321 r = -ENOMEM;
322 goto fail;
323 }
324
325 key[n_key++] = c;
326 }
a5c32cff 327
f73141d7
LP
328 break;
329
f73141d7 330 case PRE_VALUE:
98f59e59 331 if (strchr(newline, c)) {
f73141d7 332 state = PRE_KEY;
335c46b5 333 line ++;
f73141d7 334 key[n_key] = 0;
a5c32cff 335
f73141d7
LP
336 if (value)
337 value[n_value] = 0;
a5c32cff 338
ebc05a09 339 /* strip trailing whitespace from key */
2b77f67e
LP
340 if (last_key_whitespace != (size_t) -1)
341 key[last_key_whitespace] = 0;
ebc05a09 342
335c46b5 343 r = push(fname, line, key, value, userdata);
f73141d7
LP
344 if (r < 0)
345 goto fail;
346
347 n_key = 0;
348 value = NULL;
349 value_alloc = n_value = 0;
2b77f67e 350
f73141d7
LP
351 } else if (c == '\'')
352 state = SINGLE_QUOTE_VALUE;
353 else if (c == '\"')
354 state = DOUBLE_QUOTE_VALUE;
355 else if (c == '\\')
356 state = VALUE_ESCAPE;
357 else if (!strchr(WHITESPACE, c)) {
358 state = VALUE;
359
360 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
361 r = -ENOMEM;
362 goto fail;
363 }
364
365 value[n_value++] = c;
366 }
367
368 break;
369
370 case VALUE:
371 if (strchr(newline, c)) {
372 state = PRE_KEY;
335c46b5 373 line ++;
98f59e59 374
f73141d7
LP
375 key[n_key] = 0;
376
377 if (value)
378 value[n_value] = 0;
379
2b77f67e
LP
380 /* Chomp off trailing whitespace from value */
381 if (last_value_whitespace != (size_t) -1)
382 value[last_value_whitespace] = 0;
f73141d7 383
ebc05a09 384 /* strip trailing whitespace from key */
2b77f67e
LP
385 if (last_key_whitespace != (size_t) -1)
386 key[last_key_whitespace] = 0;
ebc05a09 387
335c46b5 388 r = push(fname, line, key, value, userdata);
f73141d7
LP
389 if (r < 0)
390 goto fail;
391
392 n_key = 0;
393 value = NULL;
394 value_alloc = n_value = 0;
2b77f67e 395
f73141d7
LP
396 } else if (c == '\\') {
397 state = VALUE_ESCAPE;
2b77f67e 398 last_value_whitespace = (size_t) -1;
f73141d7
LP
399 } else {
400 if (!strchr(WHITESPACE, c))
2b77f67e
LP
401 last_value_whitespace = (size_t) -1;
402 else if (last_value_whitespace == (size_t) -1)
403 last_value_whitespace = n_value;
f73141d7
LP
404
405 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
406 r = -ENOMEM;
407 goto fail;
408 }
409
410 value[n_value++] = c;
411 }
412
413 break;
414
415 case VALUE_ESCAPE:
416 state = VALUE;
417
418 if (!strchr(newline, c)) {
419 /* Escaped newlines we eat up entirely */
420 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
421 r = -ENOMEM;
422 goto fail;
423 }
424
425 value[n_value++] = c;
426 }
427 break;
428
429 case SINGLE_QUOTE_VALUE:
430 if (c == '\'')
431 state = PRE_VALUE;
432 else if (c == '\\')
433 state = SINGLE_QUOTE_VALUE_ESCAPE;
434 else {
435 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
436 r = -ENOMEM;
437 goto fail;
438 }
a5c32cff 439
f73141d7
LP
440 value[n_value++] = c;
441 }
a5c32cff 442
f73141d7 443 break;
a5c32cff 444
f73141d7
LP
445 case SINGLE_QUOTE_VALUE_ESCAPE:
446 state = SINGLE_QUOTE_VALUE;
a5c32cff 447
f73141d7
LP
448 if (!strchr(newline, c)) {
449 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
a5c32cff 450 r = -ENOMEM;
a5c32cff
HH
451 goto fail;
452 }
453
f73141d7
LP
454 value[n_value++] = c;
455 }
456 break;
457
458 case DOUBLE_QUOTE_VALUE:
459 if (c == '\"')
460 state = PRE_VALUE;
461 else if (c == '\\')
462 state = DOUBLE_QUOTE_VALUE_ESCAPE;
463 else {
464 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
465 r = -ENOMEM;
466 goto fail;
a5c32cff
HH
467 }
468
f73141d7
LP
469 value[n_value++] = c;
470 }
471
472 break;
473
474 case DOUBLE_QUOTE_VALUE_ESCAPE:
475 state = DOUBLE_QUOTE_VALUE;
a5c32cff 476
f73141d7
LP
477 if (!strchr(newline, c)) {
478 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
479 r = -ENOMEM;
480 goto fail;
481 }
a5c32cff 482
f73141d7 483 value[n_value++] = c;
a5c32cff 484 }
f73141d7
LP
485 break;
486
487 case COMMENT:
488 if (c == '\\')
489 state = COMMENT_ESCAPE;
335c46b5 490 else if (strchr(newline, c)) {
f73141d7 491 state = PRE_KEY;
335c46b5
ZJS
492 line ++;
493 }
f73141d7
LP
494 break;
495
496 case COMMENT_ESCAPE:
497 state = COMMENT;
498 break;
a5c32cff 499 }
f73141d7
LP
500 }
501
502 if (state == PRE_VALUE ||
503 state == VALUE ||
504 state == VALUE_ESCAPE ||
505 state == SINGLE_QUOTE_VALUE ||
506 state == SINGLE_QUOTE_VALUE_ESCAPE ||
507 state == DOUBLE_QUOTE_VALUE ||
508 state == DOUBLE_QUOTE_VALUE_ESCAPE) {
a5c32cff 509
f73141d7
LP
510 key[n_key] = 0;
511
512 if (value)
513 value[n_value] = 0;
514
2b77f67e
LP
515 if (state == VALUE)
516 if (last_value_whitespace != (size_t) -1)
517 value[last_value_whitespace] = 0;
518
ebc05a09 519 /* strip trailing whitespace from key */
2b77f67e
LP
520 if (last_key_whitespace != (size_t) -1)
521 key[last_key_whitespace] = 0;
ebc05a09 522
335c46b5 523 r = push(fname, line, key, value, userdata);
f73141d7
LP
524 if (r < 0)
525 goto fail;
a5c32cff
HH
526 }
527
f73141d7
LP
528 return 0;
529
a5c32cff 530fail:
f73141d7 531 free(value);
a5c32cff
HH
532 return r;
533}
534
335c46b5
ZJS
535static int parse_env_file_push(const char *filename, unsigned line,
536 const char *key, char *value, void *userdata) {
335c46b5 537
f27f0e21
GB
538 const char *k;
539 va_list aq, *ap = userdata;
540
541 if (!utf8_is_valid(key)) {
550a40ec
ZJS
542 _cleanup_free_ char *p = utf8_escape_invalid(key);
543
544 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.",
545 filename, line, p);
f27f0e21
GB
546 return -EINVAL;
547 }
548
549 if (value && !utf8_is_valid(value)) {
550a40ec
ZJS
550 _cleanup_free_ char *p = utf8_escape_invalid(value);
551
f27f0e21 552 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.",
550a40ec 553 filename, line, key, p);
f27f0e21
GB
554 return -EINVAL;
555 }
a5c32cff 556
f27f0e21 557 va_copy(aq, *ap);
a5c32cff 558
f27f0e21
GB
559 while ((k = va_arg(aq, const char *))) {
560 char **v;
a5c32cff 561
f27f0e21 562 v = va_arg(aq, char **);
a5c32cff 563
f27f0e21
GB
564 if (streq(key, k)) {
565 va_end(aq);
566 free(*v);
567 *v = value;
568 return 1;
f73141d7 569 }
335c46b5 570 }
a5c32cff 571
f27f0e21 572 va_end(aq);
f73141d7
LP
573 free(value);
574 return 0;
575}
a5c32cff 576
f73141d7
LP
577int parse_env_file(
578 const char *fname,
579 const char *newline, ...) {
a5c32cff 580
f73141d7
LP
581 va_list ap;
582 int r;
a5c32cff 583
f73141d7
LP
584 if (!newline)
585 newline = NEWLINE;
a5c32cff 586
f73141d7
LP
587 va_start(ap, newline);
588 r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
589 va_end(ap);
a5c32cff 590
f73141d7
LP
591 return r;
592}
a5c32cff 593
335c46b5
ZJS
594static int load_env_file_push(const char *filename, unsigned line,
595 const char *key, char *value, void *userdata) {
f27f0e21
GB
596 char ***m = userdata;
597 char *p;
598 int r;
a5c32cff 599
f27f0e21 600 if (!utf8_is_valid(key)) {
b5d74213
ZJS
601 _cleanup_free_ char *t = utf8_escape_invalid(key);
602
f27f0e21 603 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.",
b5d74213 604 filename, line, t);
f27f0e21
GB
605 return -EINVAL;
606 }
607
608 if (value && !utf8_is_valid(value)) {
b5d74213
ZJS
609 _cleanup_free_ char *t = utf8_escape_invalid(value);
610
f27f0e21 611 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.",
b5d74213 612 filename, line, key, t);
f27f0e21
GB
613 return -EINVAL;
614 }
a5c32cff 615
f27f0e21
GB
616 p = strjoin(key, "=", strempty(value), NULL);
617 if (!p)
618 return -ENOMEM;
335c46b5 619
6e18964d
ZJS
620 r = strv_consume(m, p);
621 if (r < 0)
f27f0e21 622 return r;
a5c32cff 623
f73141d7
LP
624 free(value);
625 return 0;
626}
627
628int load_env_file(const char *fname, const char *newline, char ***rl) {
629 char **m = NULL;
630 int r;
a5c32cff 631
f73141d7
LP
632 if (!newline)
633 newline = NEWLINE;
634
635 r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
636 if (r < 0) {
637 strv_free(m);
638 return r;
a5c32cff
HH
639 }
640
641 *rl = m;
a5c32cff
HH
642 return 0;
643}
644
768100ef
LP
645static void write_env_var(FILE *f, const char *v) {
646 const char *p;
647
648 p = strchr(v, '=');
649 if (!p) {
650 /* Fallback */
651 fputs(v, f);
652 fputc('\n', f);
653 return;
654 }
655
656 p++;
657 fwrite(v, 1, p-v, f);
658
ced2d10a 659 if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
768100ef
LP
660 fputc('\"', f);
661
662 for (; *p; p++) {
ced2d10a 663 if (strchr("\'\"\\`$", *p))
768100ef
LP
664 fputc('\\', f);
665
666 fputc(*p, f);
667 }
668
669 fputc('\"', f);
670 } else
671 fputs(p, f);
672
673 fputc('\n', f);
674}
675
a5c32cff 676int write_env_file(const char *fname, char **l) {
1c17cbed 677 char **i;
7fd1b19b
HH
678 _cleanup_free_ char *p = NULL;
679 _cleanup_fclose_ FILE *f = NULL;
a5c32cff
HH
680 int r;
681
682 r = fopen_temporary(fname, &f, &p);
683 if (r < 0)
684 return r;
685
686 fchmod_umask(fileno(f), 0644);
687
688 errno = 0;
768100ef
LP
689 STRV_FOREACH(i, l)
690 write_env_var(f, *i);
a5c32cff
HH
691
692 fflush(f);
693
fec6fc6b
LP
694 if (ferror(f))
695 r = errno ? -errno : -EIO;
696 else {
a5c32cff
HH
697 if (rename(p, fname) < 0)
698 r = -errno;
699 else
700 r = 0;
701 }
702
703 if (r < 0)
704 unlink(p);
705
a5c32cff
HH
706 return r;
707}
68fee104
ZJS
708
709int executable_is_script(const char *path, char **interpreter) {
710 int r;
711 char _cleanup_free_ *line = NULL;
712 int len;
713 char *ans;
714
715 assert(path);
716
717 r = read_one_line_file(path, &line);
718 if (r < 0)
719 return r;
720
721 if (!startswith(line, "#!"))
722 return 0;
723
724 ans = strstrip(line + 2);
725 len = strcspn(ans, " \t");
726
727 if (len == 0)
728 return 0;
729
730 ans = strndup(ans, len);
731 if (!ans)
732 return -ENOMEM;
733
734 *interpreter = ans;
735 return 1;
736}
69ab8088
ZJS
737
738/**
0a7b53bd
ZJS
739 * Retrieve one field from a file like /proc/self/status. pattern
740 * should start with '\n' and end with a ':'. Whitespace and zeros
741 * after the ':' will be skipped. field must be freed afterwards.
69ab8088
ZJS
742 */
743int get_status_field(const char *filename, const char *pattern, char **field) {
744 _cleanup_free_ char *status = NULL;
745 char *t;
746 size_t len;
747 int r;
748
749 assert(filename);
7ff7394d 750 assert(pattern);
69ab8088
ZJS
751 assert(field);
752
753 r = read_full_file(filename, &status, NULL);
754 if (r < 0)
755 return r;
756
757 t = strstr(status, pattern);
758 if (!t)
759 return -ENOENT;
760
761 t += strlen(pattern);
4ec29144 762 if (*t) {
1e5413f7
ZJS
763 t += strspn(t, " \t");
764
765 /* Also skip zeros, because when this is used for
766 * capabilities, we don't want the zeros. This way the
767 * same capability set always maps to the same string,
768 * irrespective of the total capability set size. For
769 * other numbers it shouldn't matter. */
770 t += strspn(t, "0");
4ec29144
ZJS
771 /* Back off one char if there's nothing but whitespace
772 and zeros */
1e5413f7 773 if (!*t || isspace(*t))
4ec29144
ZJS
774 t --;
775 }
69ab8088
ZJS
776
777 len = strcspn(t, WHITESPACE);
778
779 *field = strndup(t, len);
780 if (!*field)
781 return -ENOMEM;
782
783 return 0;
784}