github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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  }