chiark / gitweb /
Properly check for overflow in offsets
[elogind.git] / src / shared / fileio.c
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>
23 #include "fileio.h"
24 #include "util.h"
25 #include "strv.h"
26
27
28 int write_string_to_file(FILE *f, const char *line) {
29         errno = 0;
30         fputs(line, f);
31         if (!endswith(line, "\n"))
32                 fputc('\n', f);
33
34         fflush(f);
35
36         if (ferror(f))
37                 return errno ? -errno : -EIO;
38
39         return 0;
40 }
41
42 int write_string_file(const char *fn, const char *line) {
43         _cleanup_fclose_ FILE *f = NULL;
44
45         assert(fn);
46         assert(line);
47
48         f = fopen(fn, "we");
49         if (!f)
50                 return -errno;
51
52         return write_string_to_file(f, line);
53 }
54
55 int write_string_file_atomic(const char *fn, const char *line) {
56         _cleanup_fclose_ FILE *f = NULL;
57         _cleanup_free_ char *p = NULL;
58         int r;
59
60         assert(fn);
61         assert(line);
62
63         r = fopen_temporary(fn, &f, &p);
64         if (r < 0)
65                 return r;
66
67         fchmod_umask(fileno(f), 0644);
68
69         errno = 0;
70         fputs(line, f);
71         if (!endswith(line, "\n"))
72                 fputc('\n', f);
73
74         fflush(f);
75
76         if (ferror(f))
77                 r = errno ? -errno : -EIO;
78         else {
79                 if (rename(p, fn) < 0)
80                         r = -errno;
81                 else
82                         r = 0;
83         }
84
85         if (r < 0)
86                 unlink(p);
87
88         return r;
89 }
90
91 int read_one_line_file(const char *fn, char **line) {
92         _cleanup_fclose_ FILE *f = NULL;
93         char t[LINE_MAX], *c;
94
95         assert(fn);
96         assert(line);
97
98         f = fopen(fn, "re");
99         if (!f)
100                 return -errno;
101
102         if (!fgets(t, sizeof(t), f)) {
103
104                 if (ferror(f))
105                         return errno ? -errno : -EIO;
106
107                 t[0] = 0;
108         }
109
110         c = strdup(t);
111         if (!c)
112                 return -ENOMEM;
113         truncate_nl(c);
114
115         *line = c;
116         return 0;
117 }
118
119 int read_full_file(const char *fn, char **contents, size_t *size) {
120         _cleanup_fclose_ FILE *f = NULL;
121         size_t n, l;
122         _cleanup_free_ char *buf = NULL;
123         struct stat st;
124
125         assert(fn);
126         assert(contents);
127
128         f = fopen(fn, "re");
129         if (!f)
130                 return -errno;
131
132         if (fstat(fileno(f), &st) < 0)
133                 return -errno;
134
135         /* Safety check */
136         if (st.st_size > 4*1024*1024)
137                 return -E2BIG;
138
139         n = st.st_size > 0 ? st.st_size : LINE_MAX;
140         l = 0;
141
142         for (;;) {
143                 char *t;
144                 size_t k;
145
146                 t = realloc(buf, n+1);
147                 if (!t)
148                         return -ENOMEM;
149
150                 buf = t;
151                 k = fread(buf + l, 1, n - l, f);
152
153                 if (k <= 0) {
154                         if (ferror(f))
155                                 return -errno;
156
157                         break;
158                 }
159
160                 l += k;
161                 n *= 2;
162
163                 /* Safety check */
164                 if (n > 4*1024*1024)
165                         return -E2BIG;
166         }
167
168         buf[l] = 0;
169         *contents = buf;
170         buf = NULL;
171
172         if (size)
173                 *size = l;
174
175         return 0;
176 }
177
178 static int parse_env_file_internal(
179                 const char *fname,
180                 const char *newline,
181                 int (*push) (const char *key, char *value, void *userdata),
182                 void *userdata) {
183
184         _cleanup_free_ char *contents = NULL, *key = NULL;
185         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;
186         char *p, *value = NULL;
187         int r;
188
189         enum {
190                 PRE_KEY,
191                 KEY,
192                 PRE_VALUE,
193                 VALUE,
194                 VALUE_ESCAPE,
195                 SINGLE_QUOTE_VALUE,
196                 SINGLE_QUOTE_VALUE_ESCAPE,
197                 DOUBLE_QUOTE_VALUE,
198                 DOUBLE_QUOTE_VALUE_ESCAPE,
199                 COMMENT,
200                 COMMENT_ESCAPE
201         } state = PRE_KEY;
202
203         assert(fname);
204         assert(newline);
205
206         r = read_full_file(fname, &contents, NULL);
207         if (r < 0)
208                 return r;
209
210         for (p = contents; *p; p++) {
211                 char c = *p;
212
213                 switch (state) {
214
215                 case PRE_KEY:
216                         if (strchr(COMMENTS, c))
217                                 state = COMMENT;
218                         else if (!strchr(WHITESPACE, c)) {
219                                 state = KEY;
220                                 last_key_whitespace = (size_t) -1;
221
222                                 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
223                                         r = -ENOMEM;
224                                         goto fail;
225                                 }
226
227                                 key[n_key++] = c;
228                         }
229                         break;
230
231                 case KEY:
232                         if (strchr(newline, c)) {
233                                 state = PRE_KEY;
234                                 n_key = 0;
235                         } else if (c == '=') {
236                                 state = PRE_VALUE;
237                                 last_value_whitespace = (size_t) -1;
238                         } else {
239                                 if (!strchr(WHITESPACE, c))
240                                         last_key_whitespace = (size_t) -1;
241                                 else if (last_key_whitespace == (size_t) -1)
242                                          last_key_whitespace = n_key;
243
244                                 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
245                                         r = -ENOMEM;
246                                         goto fail;
247                                 }
248
249                                 key[n_key++] = c;
250                         }
251
252                         break;
253
254                 case PRE_VALUE:
255                         if (strchr(newline, c)) {
256                                 state = PRE_KEY;
257                                 key[n_key] = 0;
258
259                                 if (value)
260                                         value[n_value] = 0;
261
262                                 /* strip trailing whitespace from key */
263                                 if (last_key_whitespace != (size_t) -1)
264                                         key[last_key_whitespace] = 0;
265
266                                 r = push(key, value, userdata);
267                                 if (r < 0)
268                                         goto fail;
269
270                                 n_key = 0;
271                                 value = NULL;
272                                 value_alloc = n_value = 0;
273
274                         } else if (c == '\'')
275                                 state = SINGLE_QUOTE_VALUE;
276                         else if (c == '\"')
277                                 state = DOUBLE_QUOTE_VALUE;
278                         else if (c == '\\')
279                                 state = VALUE_ESCAPE;
280                         else if (!strchr(WHITESPACE, c)) {
281                                 state = VALUE;
282
283                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
284                                         r = -ENOMEM;
285                                         goto fail;
286                                 }
287
288                                 value[n_value++] = c;
289                         }
290
291                         break;
292
293                 case VALUE:
294                         if (strchr(newline, c)) {
295                                 state = PRE_KEY;
296
297                                 key[n_key] = 0;
298
299                                 if (value)
300                                         value[n_value] = 0;
301
302                                 /* Chomp off trailing whitespace from value */
303                                 if (last_value_whitespace != (size_t) -1)
304                                         value[last_value_whitespace] = 0;
305
306                                 /* strip trailing whitespace from key */
307                                 if (last_key_whitespace != (size_t) -1)
308                                         key[last_key_whitespace] = 0;
309
310                                 r = push(key, value, userdata);
311                                 if (r < 0)
312                                         goto fail;
313
314                                 n_key = 0;
315                                 value = NULL;
316                                 value_alloc = n_value = 0;
317
318                         } else if (c == '\\') {
319                                 state = VALUE_ESCAPE;
320                                 last_value_whitespace = (size_t) -1;
321                         } else {
322                                 if (!strchr(WHITESPACE, c))
323                                         last_value_whitespace = (size_t) -1;
324                                 else if (last_value_whitespace == (size_t) -1)
325                                         last_value_whitespace = n_value;
326
327                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
328                                         r = -ENOMEM;
329                                         goto fail;
330                                 }
331
332                                 value[n_value++] = c;
333                         }
334
335                         break;
336
337                 case VALUE_ESCAPE:
338                         state = VALUE;
339
340                         if (!strchr(newline, c)) {
341                                 /* Escaped newlines we eat up entirely */
342                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
343                                         r = -ENOMEM;
344                                         goto fail;
345                                 }
346
347                                 value[n_value++] = c;
348                         }
349                         break;
350
351                 case SINGLE_QUOTE_VALUE:
352                         if (c == '\'')
353                                 state = PRE_VALUE;
354                         else if (c == '\\')
355                                 state = SINGLE_QUOTE_VALUE_ESCAPE;
356                         else {
357                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
358                                         r = -ENOMEM;
359                                         goto fail;
360                                 }
361
362                                 value[n_value++] = c;
363                         }
364
365                         break;
366
367                 case SINGLE_QUOTE_VALUE_ESCAPE:
368                         state = SINGLE_QUOTE_VALUE;
369
370                         if (!strchr(newline, c)) {
371                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
372                                         r = -ENOMEM;
373                                         goto fail;
374                                 }
375
376                                 value[n_value++] = c;
377                         }
378                         break;
379
380                 case DOUBLE_QUOTE_VALUE:
381                         if (c == '\"')
382                                 state = PRE_VALUE;
383                         else if (c == '\\')
384                                 state = DOUBLE_QUOTE_VALUE_ESCAPE;
385                         else {
386                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
387                                         r = -ENOMEM;
388                                         goto fail;
389                                 }
390
391                                 value[n_value++] = c;
392                         }
393
394                         break;
395
396                 case DOUBLE_QUOTE_VALUE_ESCAPE:
397                         state = DOUBLE_QUOTE_VALUE;
398
399                         if (!strchr(newline, c)) {
400                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
401                                         r = -ENOMEM;
402                                         goto fail;
403                                 }
404
405                                 value[n_value++] = c;
406                         }
407                         break;
408
409                 case COMMENT:
410                         if (c == '\\')
411                                 state = COMMENT_ESCAPE;
412                         else if (strchr(newline, c))
413                                 state = PRE_KEY;
414                         break;
415
416                 case COMMENT_ESCAPE:
417                         state = COMMENT;
418                         break;
419                 }
420         }
421
422         if (state == PRE_VALUE ||
423             state == VALUE ||
424             state == VALUE_ESCAPE ||
425             state == SINGLE_QUOTE_VALUE ||
426             state == SINGLE_QUOTE_VALUE_ESCAPE ||
427             state == DOUBLE_QUOTE_VALUE ||
428             state == DOUBLE_QUOTE_VALUE_ESCAPE) {
429
430                 key[n_key] = 0;
431
432                 if (value)
433                         value[n_value] = 0;
434
435                 if (state == VALUE)
436                         if (last_value_whitespace != (size_t) -1)
437                                 value[last_value_whitespace] = 0;
438
439                 /* strip trailing whitespace from key */
440                 if (last_key_whitespace != (size_t) -1)
441                         key[last_key_whitespace] = 0;
442
443                 r = push(key, value, userdata);
444                 if (r < 0)
445                         goto fail;
446         }
447
448         return 0;
449
450 fail:
451         free(value);
452         return r;
453 }
454
455 static int parse_env_file_push(const char *key, char *value, void *userdata) {
456         const char *k;
457         va_list* ap = (va_list*) userdata;
458         va_list aq;
459
460         va_copy(aq, *ap);
461
462         while ((k = va_arg(aq, const char *))) {
463                 char **v;
464
465                 v = va_arg(aq, char **);
466
467                 if (streq(key, k)) {
468                         va_end(aq);
469                         free(*v);
470                         *v = value;
471                         return 1;
472                 }
473         }
474
475         va_end(aq);
476
477         free(value);
478         return 0;
479 }
480
481 int parse_env_file(
482                 const char *fname,
483                 const char *newline, ...) {
484
485         va_list ap;
486         int r;
487
488         if (!newline)
489                 newline = NEWLINE;
490
491         va_start(ap, newline);
492         r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
493         va_end(ap);
494
495         return r;
496 }
497
498 static int load_env_file_push(const char *key, char *value, void *userdata) {
499         char ***m = userdata;
500         char *p;
501         int r;
502
503         p = strjoin(key, "=", strempty(value), NULL);
504         if (!p)
505                 return -ENOMEM;
506
507         r = strv_push(m, p);
508         if (r < 0) {
509                 free(p);
510                 return r;
511         }
512
513         free(value);
514         return 0;
515 }
516
517 int load_env_file(const char *fname, const char *newline, char ***rl) {
518         char **m = NULL;
519         int r;
520
521         if (!newline)
522                 newline = NEWLINE;
523
524         r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
525         if (r < 0) {
526                 strv_free(m);
527                 return r;
528         }
529
530         *rl = m;
531         return 0;
532 }
533
534 static void write_env_var(FILE *f, const char *v) {
535         const char *p;
536
537         p = strchr(v, '=');
538         if (!p) {
539                 /* Fallback */
540                 fputs(v, f);
541                 fputc('\n', f);
542                 return;
543         }
544
545         p++;
546         fwrite(v, 1, p-v, f);
547
548         if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
549                 fputc('\"', f);
550
551                 for (; *p; p++) {
552                         if (strchr("\'\"\\`$", *p))
553                                 fputc('\\', f);
554
555                         fputc(*p, f);
556                 }
557
558                 fputc('\"', f);
559         } else
560                 fputs(p, f);
561
562         fputc('\n', f);
563 }
564
565 int write_env_file(const char *fname, char **l) {
566         char **i;
567         _cleanup_free_ char *p = NULL;
568         _cleanup_fclose_ FILE *f = NULL;
569         int r;
570
571         r = fopen_temporary(fname, &f, &p);
572         if (r < 0)
573                 return r;
574
575         fchmod_umask(fileno(f), 0644);
576
577         errno = 0;
578         STRV_FOREACH(i, l)
579                 write_env_var(f, *i);
580
581         fflush(f);
582
583         if (ferror(f))
584                 r = errno ? -errno : -EIO;
585         else {
586                 if (rename(p, fname) < 0)
587                         r = -errno;
588                 else
589                         r = 0;
590         }
591
592         if (r < 0)
593                 unlink(p);
594
595         return r;
596 }