github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/cmd/libsnap-confine-private/infofile.c (about) 1 /* 2 * Copyright (C) 2019 Canonical Ltd 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 3 as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 * 16 */ 17 18 #include "infofile.h" 19 20 #include <errno.h> 21 #include <stdarg.h> 22 #include <stdbool.h> 23 #include <string.h> 24 25 #include "../libsnap-confine-private/cleanup-funcs.h" 26 #include "../libsnap-confine-private/error.h" 27 #include "../libsnap-confine-private/string-utils.h" 28 #include "../libsnap-confine-private/utils.h" 29 30 int sc_infofile_get_key(FILE *stream, const char *key, char **value, sc_error **err_out) { 31 sc_error *err = NULL; 32 size_t line_size = 0; 33 char *line_buf SC_CLEANUP(sc_cleanup_string) = NULL; 34 35 if (stream == NULL) { 36 err = sc_error_init_api_misuse("stream cannot be NULL"); 37 goto out; 38 } 39 if (key == NULL) { 40 err = sc_error_init_api_misuse("key cannot be NULL"); 41 goto out; 42 } 43 if (value == NULL) { 44 err = sc_error_init_api_misuse("value cannot be NULL"); 45 goto out; 46 } 47 48 /* Store NULL in case we don't find the key. 49 * This makes the value always well-defined. */ 50 *value = NULL; 51 52 /* This loop advances through subsequent lines. */ 53 for (int lineno = 1;; ++lineno) { 54 errno = 0; 55 ssize_t nread = getline(&line_buf, &line_size, stream); 56 if (nread < 0 && errno != 0) { 57 err = sc_error_init_from_errno(errno, "cannot read beyond line %d", lineno); 58 goto out; 59 } 60 if (nread <= 0) { 61 break; /* There is nothing more to read. */ 62 } 63 /* NOTE: beyond this line the buffer is never empty (ie, nread > 0). */ 64 65 /* Guard against malformed input that may contain NUL bytes that 66 * would confuse the code below. */ 67 if (memchr(line_buf, '\0', nread) != NULL) { 68 err = sc_error_init_simple("line %d contains NUL byte", lineno); 69 goto out; 70 } 71 /* Guard against non-strictly formatted input that doesn't contain 72 * trailing newline. */ 73 if (line_buf[nread - 1] != '\n') { 74 err = sc_error_init(SC_LIBSNAP_DOMAIN, 0, "line %d does not end with a newline", lineno); 75 goto out; 76 } 77 /* Replace the trailing newline character with the NUL byte. */ 78 line_buf[nread - 1] = '\0'; 79 /* Guard against malformed input that does not contain '=' byte */ 80 char *eq_ptr = memchr(line_buf, '=', nread); 81 if (eq_ptr == NULL) { 82 err = sc_error_init_simple("line %d is not a key=value assignment", lineno); 83 goto out; 84 } 85 /* Guard against malformed input with empty key. */ 86 if (eq_ptr == line_buf) { 87 err = sc_error_init_simple("line %d contains empty key", lineno); 88 goto out; 89 } 90 /* Replace the first '=' with string terminator byte. */ 91 *eq_ptr = '\0'; 92 93 /* If the key matches the one we are looking for, store it and stop scanning. */ 94 const char *scanned_key = line_buf; 95 const char *scanned_value = eq_ptr + 1; 96 if (sc_streq(scanned_key, key)) { 97 *value = sc_strdup(scanned_value); 98 break; 99 } 100 } 101 102 out: 103 return sc_error_forward(err_out, err); 104 }