]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/fileio.c
Merge pull request #12322 from ljmf00/hwdb-accel-location-patch
[thirdparty/systemd.git] / src / basic / fileio.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <ctype.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <limits.h>
7 #include <stdarg.h>
8 #include <stdint.h>
9 #include <stdio_ext.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15
16 #include "alloc-util.h"
17 #include "fd-util.h"
18 #include "fileio.h"
19 #include "fs-util.h"
20 #include "hexdecoct.h"
21 #include "log.h"
22 #include "macro.h"
23 #include "missing.h"
24 #include "parse-util.h"
25 #include "path-util.h"
26 #include "stdio-util.h"
27 #include "string-util.h"
28 #include "tmpfile-util.h"
29
30 #define READ_FULL_BYTES_MAX (4U*1024U*1024U)
31
32 int fopen_unlocked(const char *path, const char *options, FILE **ret) {
33 assert(ret);
34
35 FILE *f = fopen(path, options);
36 if (!f)
37 return -errno;
38
39 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
40
41 *ret = f;
42 return 0;
43 }
44
45 int fdopen_unlocked(int fd, const char *options, FILE **ret) {
46 assert(ret);
47
48 FILE *f = fdopen(fd, options);
49 if (!f)
50 return -errno;
51
52 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
53
54 *ret = f;
55 return 0;
56 }
57
58 FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc) {
59 FILE *f = open_memstream(ptr, sizeloc);
60 if (!f)
61 return NULL;
62
63 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
64
65 return f;
66 }
67
68 FILE* fmemopen_unlocked(void *buf, size_t size, const char *mode) {
69 FILE *f = fmemopen(buf, size, mode);
70 if (!f)
71 return NULL;
72
73 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
74
75 return f;
76 }
77
78 int write_string_stream_ts(
79 FILE *f,
80 const char *line,
81 WriteStringFileFlags flags,
82 struct timespec *ts) {
83
84 bool needs_nl;
85 int r;
86
87 assert(f);
88 assert(line);
89
90 if (ferror(f))
91 return -EIO;
92
93 needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
94
95 if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
96 /* If STDIO buffering was disabled, then let's append the newline character to the string itself, so
97 * that the write goes out in one go, instead of two */
98
99 line = strjoina(line, "\n");
100 needs_nl = false;
101 }
102
103 if (fputs(line, f) == EOF)
104 return -errno;
105
106 if (needs_nl)
107 if (fputc('\n', f) == EOF)
108 return -errno;
109
110 if (flags & WRITE_STRING_FILE_SYNC)
111 r = fflush_sync_and_check(f);
112 else
113 r = fflush_and_check(f);
114 if (r < 0)
115 return r;
116
117 if (ts) {
118 struct timespec twice[2] = {*ts, *ts};
119
120 if (futimens(fileno(f), twice) < 0)
121 return -errno;
122 }
123
124 return 0;
125 }
126
127 static int write_string_file_atomic(
128 const char *fn,
129 const char *line,
130 WriteStringFileFlags flags,
131 struct timespec *ts) {
132
133 _cleanup_fclose_ FILE *f = NULL;
134 _cleanup_free_ char *p = NULL;
135 int r;
136
137 assert(fn);
138 assert(line);
139
140 r = fopen_temporary(fn, &f, &p);
141 if (r < 0)
142 return r;
143
144 (void) fchmod_umask(fileno(f), 0644);
145
146 r = write_string_stream_ts(f, line, flags, ts);
147 if (r < 0)
148 goto fail;
149
150 if (rename(p, fn) < 0) {
151 r = -errno;
152 goto fail;
153 }
154
155 return 0;
156
157 fail:
158 (void) unlink(p);
159 return r;
160 }
161
162 int write_string_file_ts(
163 const char *fn,
164 const char *line,
165 WriteStringFileFlags flags,
166 struct timespec *ts) {
167
168 _cleanup_fclose_ FILE *f = NULL;
169 int q, r;
170
171 assert(fn);
172 assert(line);
173
174 /* We don't know how to verify whether the file contents was already on-disk. */
175 assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC)));
176
177 if (flags & WRITE_STRING_FILE_ATOMIC) {
178 assert(flags & WRITE_STRING_FILE_CREATE);
179
180 r = write_string_file_atomic(fn, line, flags, ts);
181 if (r < 0)
182 goto fail;
183
184 return r;
185 } else
186 assert(!ts);
187
188 if (flags & WRITE_STRING_FILE_CREATE) {
189 r = fopen_unlocked(fn, "we", &f);
190 if (r < 0)
191 goto fail;
192 } else {
193 int fd;
194
195 /* We manually build our own version of fopen(..., "we") that
196 * works without O_CREAT */
197 fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY | ((flags & WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0));
198 if (fd < 0) {
199 r = -errno;
200 goto fail;
201 }
202
203 r = fdopen_unlocked(fd, "w", &f);
204 if (r < 0) {
205 safe_close(fd);
206 goto fail;
207 }
208 }
209
210 if (flags & WRITE_STRING_FILE_DISABLE_BUFFER)
211 setvbuf(f, NULL, _IONBF, 0);
212
213 r = write_string_stream_ts(f, line, flags, ts);
214 if (r < 0)
215 goto fail;
216
217 return 0;
218
219 fail:
220 if (!(flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE))
221 return r;
222
223 f = safe_fclose(f);
224
225 /* OK, the operation failed, but let's see if the right
226 * contents in place already. If so, eat up the error. */
227
228 q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
229 if (q <= 0)
230 return r;
231
232 return 0;
233 }
234
235 int write_string_filef(
236 const char *fn,
237 WriteStringFileFlags flags,
238 const char *format, ...) {
239
240 _cleanup_free_ char *p = NULL;
241 va_list ap;
242 int r;
243
244 va_start(ap, format);
245 r = vasprintf(&p, format, ap);
246 va_end(ap);
247
248 if (r < 0)
249 return -ENOMEM;
250
251 return write_string_file(fn, p, flags);
252 }
253
254 int read_one_line_file(const char *fn, char **line) {
255 _cleanup_fclose_ FILE *f = NULL;
256 int r;
257
258 assert(fn);
259 assert(line);
260
261 r = fopen_unlocked(fn, "re", &f);
262 if (r < 0)
263 return r;
264
265 return read_line(f, LONG_LINE_MAX, line);
266 }
267
268 int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
269 _cleanup_fclose_ FILE *f = NULL;
270 _cleanup_free_ char *buf = NULL;
271 size_t l, k;
272 int r;
273
274 assert(fn);
275 assert(blob);
276
277 l = strlen(blob);
278
279 if (accept_extra_nl && endswith(blob, "\n"))
280 accept_extra_nl = false;
281
282 buf = malloc(l + accept_extra_nl + 1);
283 if (!buf)
284 return -ENOMEM;
285
286 r = fopen_unlocked(fn, "re", &f);
287 if (r < 0)
288 return r;
289
290 /* We try to read one byte more than we need, so that we know whether we hit eof */
291 errno = 0;
292 k = fread(buf, 1, l + accept_extra_nl + 1, f);
293 if (ferror(f))
294 return errno > 0 ? -errno : -EIO;
295
296 if (k != l && k != l + accept_extra_nl)
297 return 0;
298 if (memcmp(buf, blob, l) != 0)
299 return 0;
300 if (k > l && buf[l] != '\n')
301 return 0;
302
303 return 1;
304 }
305
306 int read_full_stream_full(
307 FILE *f,
308 const char *filename,
309 ReadFullFileFlags flags,
310 char **ret_contents,
311 size_t *ret_size) {
312
313 _cleanup_free_ char *buf = NULL;
314 struct stat st;
315 size_t n, n_next, l;
316 int fd, r;
317
318 assert(f);
319 assert(ret_contents);
320 assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX));
321 assert(!(flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) || ret_size);
322
323 n_next = LINE_MAX; /* Start size */
324
325 fd = fileno(f);
326 if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's
327 * optimize our buffering) */
328
329 if (fstat(fd, &st) < 0)
330 return -errno;
331
332 if (S_ISREG(st.st_mode)) {
333
334 /* Safety check */
335 if (st.st_size > READ_FULL_BYTES_MAX)
336 return -E2BIG;
337
338 /* Start with the right file size, but be prepared for files from /proc which generally report a file
339 * size of 0. Note that we increase the size to read here by one, so that the first read attempt
340 * already makes us notice the EOF. */
341 if (st.st_size > 0)
342 n_next = st.st_size + 1;
343
344 if (flags & READ_FULL_FILE_SECURE)
345 (void) warn_file_is_world_accessible(filename, &st, NULL, 0);
346 }
347 }
348
349 n = l = 0;
350 for (;;) {
351 char *t;
352 size_t k;
353
354 if (flags & READ_FULL_FILE_SECURE) {
355 t = malloc(n_next + 1);
356 if (!t) {
357 r = -ENOMEM;
358 goto finalize;
359 }
360 memcpy_safe(t, buf, n);
361 explicit_bzero_safe(buf, n);
362 buf = mfree(buf);
363 } else {
364 t = realloc(buf, n_next + 1);
365 if (!t)
366 return -ENOMEM;
367 }
368
369 buf = t;
370 n = n_next;
371
372 errno = 0;
373 k = fread(buf + l, 1, n - l, f);
374 if (k > 0)
375 l += k;
376
377 if (ferror(f)) {
378 r = errno > 0 ? -errno : -EIO;
379 goto finalize;
380 }
381
382 if (feof(f))
383 break;
384
385 /* We aren't expecting fread() to return a short read outside
386 * of (error && eof), assert buffer is full and enlarge buffer.
387 */
388 assert(l == n);
389
390 /* Safety check */
391 if (n >= READ_FULL_BYTES_MAX) {
392 r = -E2BIG;
393 goto finalize;
394 }
395
396 n_next = MIN(n * 2, READ_FULL_BYTES_MAX);
397 }
398
399 if (flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) {
400 buf[l++] = 0;
401 if (flags & READ_FULL_FILE_UNBASE64)
402 r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size);
403 else
404 r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size);
405 goto finalize;
406 }
407
408 if (!ret_size) {
409 /* Safety check: if the caller doesn't want to know the size of what we just read it will rely on the
410 * trailing NUL byte. But if there's an embedded NUL byte, then we should refuse operation as otherwise
411 * there'd be ambiguity about what we just read. */
412
413 if (memchr(buf, 0, l)) {
414 r = -EBADMSG;
415 goto finalize;
416 }
417 }
418
419 buf[l] = 0;
420 *ret_contents = TAKE_PTR(buf);
421
422 if (ret_size)
423 *ret_size = l;
424
425 return 0;
426
427 finalize:
428 if (flags & READ_FULL_FILE_SECURE)
429 explicit_bzero_safe(buf, n);
430
431 return r;
432 }
433
434 int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) {
435 _cleanup_fclose_ FILE *f = NULL;
436 int r;
437
438 assert(filename);
439 assert(contents);
440
441 r = fopen_unlocked(filename, "re", &f);
442 if (r < 0)
443 return r;
444
445 return read_full_stream_full(f, filename, flags, contents, size);
446 }
447
448 int executable_is_script(const char *path, char **interpreter) {
449 _cleanup_free_ char *line = NULL;
450 size_t len;
451 char *ans;
452 int r;
453
454 assert(path);
455
456 r = read_one_line_file(path, &line);
457 if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */
458 return 0;
459 if (r < 0)
460 return r;
461
462 if (!startswith(line, "#!"))
463 return 0;
464
465 ans = strstrip(line + 2);
466 len = strcspn(ans, " \t");
467
468 if (len == 0)
469 return 0;
470
471 ans = strndup(ans, len);
472 if (!ans)
473 return -ENOMEM;
474
475 *interpreter = ans;
476 return 1;
477 }
478
479 /**
480 * Retrieve one field from a file like /proc/self/status. pattern
481 * should not include whitespace or the delimiter (':'). pattern matches only
482 * the beginning of a line. Whitespace before ':' is skipped. Whitespace and
483 * zeros after the ':' will be skipped. field must be freed afterwards.
484 * terminator specifies the terminating characters of the field value (not
485 * included in the value).
486 */
487 int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
488 _cleanup_free_ char *status = NULL;
489 char *t, *f;
490 size_t len;
491 int r;
492
493 assert(terminator);
494 assert(filename);
495 assert(pattern);
496 assert(field);
497
498 r = read_full_file(filename, &status, NULL);
499 if (r < 0)
500 return r;
501
502 t = status;
503
504 do {
505 bool pattern_ok;
506
507 do {
508 t = strstr(t, pattern);
509 if (!t)
510 return -ENOENT;
511
512 /* Check that pattern occurs in beginning of line. */
513 pattern_ok = (t == status || t[-1] == '\n');
514
515 t += strlen(pattern);
516
517 } while (!pattern_ok);
518
519 t += strspn(t, " \t");
520 if (!*t)
521 return -ENOENT;
522
523 } while (*t != ':');
524
525 t++;
526
527 if (*t) {
528 t += strspn(t, " \t");
529
530 /* Also skip zeros, because when this is used for
531 * capabilities, we don't want the zeros. This way the
532 * same capability set always maps to the same string,
533 * irrespective of the total capability set size. For
534 * other numbers it shouldn't matter. */
535 t += strspn(t, "0");
536 /* Back off one char if there's nothing but whitespace
537 and zeros */
538 if (!*t || isspace(*t))
539 t--;
540 }
541
542 len = strcspn(t, terminator);
543
544 f = strndup(t, len);
545 if (!f)
546 return -ENOMEM;
547
548 *field = f;
549 return 0;
550 }
551
552 DIR *xopendirat(int fd, const char *name, int flags) {
553 int nfd;
554 DIR *d;
555
556 assert(!(flags & O_CREAT));
557
558 nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
559 if (nfd < 0)
560 return NULL;
561
562 d = fdopendir(nfd);
563 if (!d) {
564 safe_close(nfd);
565 return NULL;
566 }
567
568 return d;
569 }
570
571 static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
572 char **i;
573
574 assert(path);
575 assert(mode);
576 assert(_f);
577
578 if (!path_strv_resolve_uniq(search, root))
579 return -ENOMEM;
580
581 STRV_FOREACH(i, search) {
582 _cleanup_free_ char *p = NULL;
583 FILE *f;
584
585 if (root)
586 p = strjoin(root, *i, "/", path);
587 else
588 p = strjoin(*i, "/", path);
589 if (!p)
590 return -ENOMEM;
591
592 f = fopen(p, mode);
593 if (f) {
594 *_f = f;
595 return 0;
596 }
597
598 if (errno != ENOENT)
599 return -errno;
600 }
601
602 return -ENOENT;
603 }
604
605 int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) {
606 _cleanup_strv_free_ char **copy = NULL;
607
608 assert(path);
609 assert(mode);
610 assert(_f);
611
612 if (path_is_absolute(path)) {
613 FILE *f;
614
615 f = fopen(path, mode);
616 if (f) {
617 *_f = f;
618 return 0;
619 }
620
621 return -errno;
622 }
623
624 copy = strv_copy((char**) search);
625 if (!copy)
626 return -ENOMEM;
627
628 return search_and_fopen_internal(path, mode, root, copy, _f);
629 }
630
631 int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) {
632 _cleanup_strv_free_ char **s = NULL;
633
634 if (path_is_absolute(path)) {
635 FILE *f;
636
637 f = fopen(path, mode);
638 if (f) {
639 *_f = f;
640 return 0;
641 }
642
643 return -errno;
644 }
645
646 s = strv_split_nulstr(search);
647 if (!s)
648 return -ENOMEM;
649
650 return search_and_fopen_internal(path, mode, root, s, _f);
651 }
652
653 int fflush_and_check(FILE *f) {
654 assert(f);
655
656 errno = 0;
657 fflush(f);
658
659 if (ferror(f))
660 return errno > 0 ? -errno : -EIO;
661
662 return 0;
663 }
664
665 int fflush_sync_and_check(FILE *f) {
666 int r;
667
668 assert(f);
669
670 r = fflush_and_check(f);
671 if (r < 0)
672 return r;
673
674 if (fsync(fileno(f)) < 0)
675 return -errno;
676
677 r = fsync_directory_of_file(fileno(f));
678 if (r < 0)
679 return r;
680
681 return 0;
682 }
683
684 int write_timestamp_file_atomic(const char *fn, usec_t n) {
685 char ln[DECIMAL_STR_MAX(n)+2];
686
687 /* Creates a "timestamp" file, that contains nothing but a
688 * usec_t timestamp, formatted in ASCII. */
689
690 if (n <= 0 || n >= USEC_INFINITY)
691 return -ERANGE;
692
693 xsprintf(ln, USEC_FMT "\n", n);
694
695 return write_string_file(fn, ln, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
696 }
697
698 int read_timestamp_file(const char *fn, usec_t *ret) {
699 _cleanup_free_ char *ln = NULL;
700 uint64_t t;
701 int r;
702
703 r = read_one_line_file(fn, &ln);
704 if (r < 0)
705 return r;
706
707 r = safe_atou64(ln, &t);
708 if (r < 0)
709 return r;
710
711 if (t <= 0 || t >= (uint64_t) USEC_INFINITY)
712 return -ERANGE;
713
714 *ret = (usec_t) t;
715 return 0;
716 }
717
718 int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) {
719 int r;
720
721 assert(s);
722
723 /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter
724 * when specified shall initially point to a boolean variable initialized to false. It is set to true after the
725 * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each
726 * element, but not before the first one. */
727
728 if (!f)
729 f = stdout;
730
731 if (space) {
732 if (!separator)
733 separator = " ";
734
735 if (*space) {
736 r = fputs(separator, f);
737 if (r < 0)
738 return r;
739 }
740
741 *space = true;
742 }
743
744 return fputs(s, f);
745 }
746
747 /* A bitmask of the EOL markers we know */
748 typedef enum EndOfLineMarker {
749 EOL_NONE = 0,
750 EOL_ZERO = 1 << 0, /* \0 (aka NUL) */
751 EOL_TEN = 1 << 1, /* \n (aka NL, aka LF) */
752 EOL_THIRTEEN = 1 << 2, /* \r (aka CR) */
753 } EndOfLineMarker;
754
755 static EndOfLineMarker categorize_eol(char c, ReadLineFlags flags) {
756
757 if (!IN_SET(flags, READ_LINE_ONLY_NUL)) {
758 if (c == '\n')
759 return EOL_TEN;
760 if (c == '\r')
761 return EOL_THIRTEEN;
762 }
763
764 if (c == '\0')
765 return EOL_ZERO;
766
767 return EOL_NONE;
768 }
769
770 DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile);
771
772 int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
773 size_t n = 0, allocated = 0, count = 0;
774 _cleanup_free_ char *buffer = NULL;
775 int r;
776
777 assert(f);
778
779 /* Something like a bounded version of getline().
780 *
781 * Considers EOF, \n, \r and \0 end of line delimiters (or combinations of these), and does not include these
782 * delimiters in the string returned. Specifically, recognizes the following combinations of markers as line
783 * endings:
784 *
785 * • \n (UNIX)
786 * • \r (old MacOS)
787 * • \0 (C strings)
788 * • \n\0
789 * • \r\0
790 * • \r\n (Windows)
791 * • \n\r
792 * • \r\n\0
793 * • \n\r\0
794 *
795 * Returns the number of bytes read from the files (i.e. including delimiters — this hence usually differs from
796 * the number of characters in the returned string). When EOF is hit, 0 is returned.
797 *
798 * The input parameter limit is the maximum numbers of characters in the returned string, i.e. excluding
799 * delimiters. If the limit is hit we fail and return -ENOBUFS.
800 *
801 * If a line shall be skipped ret may be initialized as NULL. */
802
803 if (ret) {
804 if (!GREEDY_REALLOC(buffer, allocated, 1))
805 return -ENOMEM;
806 }
807
808 {
809 _unused_ _cleanup_(funlockfilep) FILE *flocked = f;
810 EndOfLineMarker previous_eol = EOL_NONE;
811 flockfile(f);
812
813 for (;;) {
814 EndOfLineMarker eol;
815 char c;
816
817 if (n >= limit)
818 return -ENOBUFS;
819
820 if (count >= INT_MAX) /* We couldn't return the counter anymore as "int", hence refuse this */
821 return -ENOBUFS;
822
823 r = safe_fgetc(f, &c);
824 if (r < 0)
825 return r;
826 if (r == 0) /* EOF is definitely EOL */
827 break;
828
829 eol = categorize_eol(c, flags);
830
831 if (FLAGS_SET(previous_eol, EOL_ZERO) ||
832 (eol == EOL_NONE && previous_eol != EOL_NONE) ||
833 (eol != EOL_NONE && (previous_eol & eol) != 0)) {
834 /* Previous char was a NUL? This is not an EOL, but the previous char was? This type of
835 * EOL marker has been seen right before? In either of these three cases we are
836 * done. But first, let's put this character back in the queue. (Note that we have to
837 * cast this to (unsigned char) here as ungetc() expects a positive 'int', and if we
838 * are on an architecture where 'char' equals 'signed char' we need to ensure we don't
839 * pass a negative value here. That said, to complicate things further ungetc() is
840 * actually happy with most negative characters and implicitly casts them back to
841 * positive ones as needed, except for \xff (aka -1, aka EOF), which it refuses. What a
842 * godawful API!) */
843 assert_se(ungetc((unsigned char) c, f) != EOF);
844 break;
845 }
846
847 count++;
848
849 if (eol != EOL_NONE) {
850 previous_eol |= eol;
851 continue;
852 }
853
854 if (ret) {
855 if (!GREEDY_REALLOC(buffer, allocated, n + 2))
856 return -ENOMEM;
857
858 buffer[n] = c;
859 }
860
861 n++;
862 }
863 }
864
865 if (ret) {
866 buffer[n] = 0;
867
868 *ret = TAKE_PTR(buffer);
869 }
870
871 return (int) count;
872 }
873
874 int safe_fgetc(FILE *f, char *ret) {
875 int k;
876
877 assert(f);
878
879 /* A safer version of plain fgetc(): let's propagate the error that happened while reading as such, and
880 * separate the EOF condition from the byte read, to avoid those confusion signed/unsigned issues fgetc()
881 * has. */
882
883 errno = 0;
884 k = fgetc(f);
885 if (k == EOF) {
886 if (ferror(f))
887 return errno > 0 ? -errno : -EIO;
888
889 if (ret)
890 *ret = 0;
891
892 return 0;
893 }
894
895 if (ret)
896 *ret = k;
897
898 return 1;
899 }
900
901 int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line) {
902 struct stat _st;
903
904 if (!filename)
905 return 0;
906
907 if (!st) {
908 if (stat(filename, &_st) < 0)
909 return -errno;
910 st = &_st;
911 }
912
913 if ((st->st_mode & S_IRWXO) == 0)
914 return 0;
915
916 if (unit)
917 log_syntax(unit, LOG_WARNING, filename, line, 0,
918 "%s has %04o mode that is too permissive, please adjust the access mode.",
919 filename, st->st_mode & 07777);
920 else
921 log_warning("%s has %04o mode that is too permissive, please adjust the access mode.",
922 filename, st->st_mode & 07777);
923 return 0;
924 }