bool read_procfs_cap_bits()

in antlir/nspawn_in_subvol/clonecaps/clonecaps.c [133:219]


bool read_procfs_cap_bits(const char* status_filename, cap_bits_t* cap_bits) {
  memset(cap_bits, 0, sizeof(*cap_bits));

  // We'll compare these to make sure we saw all the expected procfs lines.
  int expected_cap_types = CAPNG_INHERITABLE | CAPNG_PERMITTED |
      CAPNG_EFFECTIVE
      // NB: Antlir doesn't really like kernels older than 4.3, so
      // I did not bother to conditionalize the availability of CapAmb.
      | CAPNG_AMBIENT | CAPNG_BOUNDING_SET;

  int actual_cap_types = 0;

  FILE* status_file = fopen(status_filename, "re");
  if (status_file == NULL) {
    perror(status_filename);
    return false;
  }
  // Not all lines are under 64 bytes (the max length is ~unbounded thanks
  // to groups), but `Cap*:` lines will be, for the foreseeable future.
  // As of capability API v3, they are at 25 bytes including newline.
  char buf[64];
  bool continuing_line = false; // Previous `buf` lacked `\n`.
  while (fgets(buf, sizeof(buf), status_file)) {
    bool skip_buf = continuing_line;
    continuing_line = (buf[strlen(buf) - 1] != '\n');
    if (skip_buf) {
      continue; // Nothing to see here, this is not a `Cap*:` line.
    }
    // Both values are populated by `match`
    int cap_type = 0;
    int pref_len = 0;
    if (!(_match(buf, "CapInh:\t", &cap_type, CAPNG_INHERITABLE, &pref_len) ||
          _match(buf, "CapPrm:\t", &cap_type, CAPNG_PERMITTED, &pref_len) ||
          _match(buf, "CapEff:\t", &cap_type, CAPNG_EFFECTIVE, &pref_len) ||
          _match(buf, "CapBnd:\t", &cap_type, CAPNG_BOUNDING_SET, &pref_len) ||
          _match(buf, "CapAmb:\t", &cap_type, CAPNG_AMBIENT, &pref_len))) {
      continue;
    }

    // Fail on duplicate cap types in the input
    if (actual_cap_types & cap_type) {
      fprintf(
          stderr,
          "%s: Capability type %d occurred more than once\n",
          status_filename,
          cap_type);
      return false;
    }
    actual_cap_types |= cap_type;

    // Read out the bits for this capability, we'll apply them later
    char* end_of_bits = NULL;
    __u64 bits = strtoull(buf + pref_len, &end_of_bits, 16);
    // We should have read 16 hex bytes, terminated by a newline.
    if ((end_of_bits - (buf + pref_len)) != 16 || end_of_bits[0] != '\n') {
      fprintf(
          stderr,
          "%s: Failed to parse value %s for capability type %d\n",
          status_filename,
          buf + pref_len,
          cap_type);
      return false;
    }
    if (cap_type == CAPNG_INHERITABLE) {
      cap_bits->inheritable = bits;
    } else if (cap_type == CAPNG_PERMITTED) {
      cap_bits->permitted = bits;
    } else if (cap_type == CAPNG_EFFECTIVE) {
      cap_bits->effective = bits;
    } else if (cap_type == CAPNG_BOUNDING_SET) {
      cap_bits->bounding_set = bits;
    } else if (cap_type == CAPNG_AMBIENT) {
      cap_bits->ambient = bits;
    }
  }
  fclose(status_file);
  if (actual_cap_types != expected_cap_types) {
    fprintf(
        stderr,
        "%s: Missing capability types: %d vs %d\n",
        status_filename,
        actual_cap_types,
        expected_cap_types);
    return false;
  }
  return true;
}