gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/util/mount_util.cc (about)

     1  // Copyright 2021 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include "test/util/mount_util.h"
    16  
    17  #include <sys/syscall.h>
    18  #include <unistd.h>
    19  
    20  #include <cerrno>
    21  #include <string>
    22  #include <string_view>
    23  #include <vector>
    24  
    25  #include "absl/container/flat_hash_map.h"
    26  #include "absl/strings/numbers.h"
    27  #include "absl/strings/str_join.h"
    28  #include "absl/strings/str_split.h"
    29  #include "absl/types/span.h"
    30  #include "test/util/posix_error.h"
    31  
    32  namespace gvisor {
    33  namespace testing {
    34  
    35  PosixErrorOr<std::vector<ProcMountsEntry>> ProcSelfMountsEntries() {
    36    std::string content;
    37    RETURN_IF_ERRNO(GetContents("/proc/self/mounts", &content));
    38    return ProcSelfMountsEntriesFrom(content);
    39  }
    40  
    41  PosixErrorOr<std::vector<ProcMountsEntry>> ProcSelfMountsEntriesFrom(
    42      const std::string& content) {
    43    std::vector<ProcMountsEntry> entries;
    44    std::vector<std::string> lines =
    45        absl::StrSplit(content, absl::ByChar('\n'), absl::AllowEmpty());
    46    std::cerr << "<contents of /proc/self/mounts>" << std::endl;
    47    for (const std::string& line : lines) {
    48      std::cerr << line << std::endl;
    49      if (line.empty()) {
    50        continue;
    51      }
    52  
    53      // Parse a single entry from /proc/self/mounts.
    54      //
    55      // Example entries:
    56      //
    57      // sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
    58      // proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
    59      //  ^     ^    ^                ^                  ^ ^
    60      //  0     1    2                3                  4 5
    61  
    62      ProcMountsEntry entry;
    63      std::vector<std::string> fields =
    64          absl::StrSplit(line, absl::ByChar(' '), absl::AllowEmpty());
    65      if (fields.size() != 6) {
    66        return PosixError(
    67            EINVAL, absl::StrFormat("Not enough tokens, got %d, content: <<%s>>",
    68                                    fields.size(), content));
    69      }
    70  
    71      entry.spec = fields[0];
    72      entry.mount_point = fields[1];
    73      entry.fstype = fields[2];
    74      entry.mount_opts = fields[3];
    75      ASSIGN_OR_RETURN_ERRNO(entry.dump, Atoi<uint32_t>(fields[4]));
    76      ASSIGN_OR_RETURN_ERRNO(entry.fsck, Atoi<uint32_t>(fields[5]));
    77  
    78      entries.push_back(entry);
    79    }
    80    std::cerr << "<end of /proc/self/mounts>" << std::endl;
    81  
    82    return entries;
    83  }
    84  
    85  PosixErrorOr<std::vector<ProcMountInfoEntry>> ProcSelfMountInfoEntries() {
    86    std::string content;
    87    RETURN_IF_ERRNO(GetContents("/proc/self/mountinfo", &content));
    88    return ProcSelfMountInfoEntriesFrom(content);
    89  }
    90  
    91  PosixErrorOr<std::vector<ProcMountInfoEntry>> ProcSelfMountInfoEntriesFrom(
    92      const std::string& content) {
    93    std::vector<ProcMountInfoEntry> entries;
    94    std::vector<std::string> lines =
    95        absl::StrSplit(content, absl::ByChar('\n'), absl::AllowEmpty());
    96    std::cerr << "<contents of /proc/self/mountinfo>" << std::endl;
    97    for (const std::string& line : lines) {
    98      std::cerr << line << std::endl;
    99      if (line.empty()) {
   100        continue;
   101      }
   102  
   103      // Parse a single entry from /proc/self/mountinfo.
   104      //
   105      // Example entries:
   106      //
   107      // 22 28 0:20 / /sys rw,relatime shared:7 - sysfs sysfs rw
   108      // 23 28 0:21 / /proc rw,relatime shared:14 - proc proc rw
   109      // ^  ^    ^  ^   ^        ^         ^      ^  ^    ^   ^
   110      // 0  1    2  3   4        5         6      7  8    9   10
   111  
   112      ProcMountInfoEntry entry;
   113      std::vector<std::string> fields =
   114          absl::StrSplit(line, absl::ByChar(' '), absl::AllowEmpty());
   115      if (fields.size() < 10 || fields.size() > 13) {
   116        return PosixError(
   117            EINVAL,
   118            absl::StrFormat("Unexpected number of tokens, got %d, line: <<%s>>",
   119                            fields.size(), line));
   120      }
   121  
   122      ASSIGN_OR_RETURN_ERRNO(entry.id, Atoi<uint64_t>(fields[0]));
   123      ASSIGN_OR_RETURN_ERRNO(entry.parent_id, Atoi<uint64_t>(fields[1]));
   124  
   125      std::vector<std::string> devs =
   126          absl::StrSplit(fields[2], absl::ByChar(':'));
   127      if (devs.size() != 2) {
   128        return PosixError(
   129            EINVAL,
   130            absl::StrFormat(
   131                "Failed to parse dev number field %s: too many tokens, got %d",
   132                fields[2], devs.size()));
   133      }
   134      ASSIGN_OR_RETURN_ERRNO(entry.major, Atoi<dev_t>(devs[0]));
   135      ASSIGN_OR_RETURN_ERRNO(entry.minor, Atoi<dev_t>(devs[1]));
   136  
   137      entry.root = fields[3];
   138      entry.mount_point = fields[4];
   139      entry.mount_opts = fields[5];
   140  
   141      // The optional field (fields[6]) may or may not be present and can have up
   142      // to 3 elements. We know based on the total number of tokens.
   143      int off = -1;
   144      if (fields.size() > 10) {
   145        int num_optional_tags = fields.size() - 10;
   146        entry.optional = absl::StrJoin(
   147            absl::MakeSpan(fields).subspan(6, num_optional_tags), " ");
   148        off += num_optional_tags;
   149      }
   150      // Field 7 is the optional field terminator char '-'.
   151      entry.fstype = fields[8 + off];
   152      entry.mount_source = fields[9 + off];
   153      entry.super_opts = fields[10 + off];
   154  
   155      entries.push_back(entry);
   156    }
   157    std::cerr << "<end of /proc/self/mountinfo>" << std::endl;
   158  
   159    return entries;
   160  }
   161  
   162  absl::flat_hash_map<std::string, std::string> ParseMountOptions(
   163      std::string mopts) {
   164    absl::flat_hash_map<std::string, std::string> entries;
   165    const std::vector<std::string> tokens =
   166        absl::StrSplit(mopts, absl::ByChar(','), absl::AllowEmpty());
   167    for (const auto& token : tokens) {
   168      std::vector<std::string> kv =
   169          absl::StrSplit(token, absl::MaxSplits('=', 1));
   170      if (kv.size() == 2) {
   171        entries[kv[0]] = kv[1];
   172      } else if (kv.size() == 1) {
   173        entries[kv[0]] = "";
   174      } else {
   175        TEST_CHECK_MSG(
   176            false,
   177            absl::StrFormat(
   178                "Invalid mount option token '%s', was split into %d subtokens",
   179                token, kv.size())
   180                .c_str());
   181      }
   182    }
   183    return entries;
   184  }
   185  
   186  PosixErrorOr<absl::flat_hash_map<std::string, std::vector<MountOptional>>>
   187  MountOptionals() {
   188    absl::flat_hash_map<std::string, std::vector<MountOptional>> optionals;
   189    ASSIGN_OR_RETURN_ERRNO(std::vector<ProcMountInfoEntry> mounts,
   190                           ProcSelfMountInfoEntries());
   191    for (const auto& e : mounts) {
   192      MountOptional opt;
   193      opt.shared = 0;
   194      opt.master = 0;
   195      opt.propagate_from = 0;
   196      std::vector<std::string_view> tags = absl::StrSplit(e.optional, ' ');
   197  
   198      for (std::string_view tag : tags) {
   199        PosixError err = ParseOptionalTag(tag, &opt);
   200        if (!err.ok()) return err;
   201      }
   202      if (optionals.contains(e.mount_point)) {
   203        optionals[e.mount_point].push_back(opt);
   204      } else {
   205        optionals[e.mount_point] = {opt};
   206      }
   207    }
   208    return optionals;
   209  }
   210  
   211  PosixError ParseOptionalTag(std::string_view tag, MountOptional* opt) {
   212    std::vector<absl::string_view> key_value =
   213        absl::StrSplit(absl::string_view(tag.data(), tag.size()), ':');
   214    if (key_value.size() != 2) return PosixError(0);
   215    if (key_value[0] == "shared") {
   216      if (!absl::SimpleAtoi(key_value[1], &opt->shared))
   217        return PosixError(EINVAL,
   218                          "could not parse shared value in optional string");
   219    } else if (key_value[0] == "master") {
   220      if (!absl::SimpleAtoi(key_value[1], &opt->master))
   221        return PosixError(EINVAL,
   222                          "could not parse master value in optional string");
   223    } else if (key_value[0] == "propagate_from") {
   224      if (!absl::SimpleAtoi(key_value[1], &opt->propagate_from))
   225        return PosixError(EINVAL,
   226                          "could not parse propagate_from in optional string");
   227    }
   228    return PosixError(0);
   229  }
   230  
   231  }  // namespace testing
   232  }  // namespace gvisor