github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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 "absl/strings/numbers.h"
    21  #include "absl/strings/str_split.h"
    22  
    23  namespace gvisor {
    24  namespace testing {
    25  
    26  PosixErrorOr<std::vector<ProcMountsEntry>> ProcSelfMountsEntries() {
    27    std::string content;
    28    RETURN_IF_ERRNO(GetContents("/proc/self/mounts", &content));
    29    return ProcSelfMountsEntriesFrom(content);
    30  }
    31  
    32  PosixErrorOr<std::vector<ProcMountsEntry>> ProcSelfMountsEntriesFrom(
    33      const std::string& content) {
    34    std::vector<ProcMountsEntry> entries;
    35    std::vector<std::string> lines =
    36        absl::StrSplit(content, absl::ByChar('\n'), absl::AllowEmpty());
    37    std::cerr << "<contents of /proc/self/mounts>" << std::endl;
    38    for (const std::string& line : lines) {
    39      std::cerr << line << std::endl;
    40      if (line.empty()) {
    41        continue;
    42      }
    43  
    44      // Parse a single entry from /proc/self/mounts.
    45      //
    46      // Example entries:
    47      //
    48      // sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
    49      // proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
    50      //  ^     ^    ^                ^                  ^ ^
    51      //  0     1    2                3                  4 5
    52  
    53      ProcMountsEntry entry;
    54      std::vector<std::string> fields =
    55          absl::StrSplit(line, absl::ByChar(' '), absl::AllowEmpty());
    56      if (fields.size() != 6) {
    57        return PosixError(
    58            EINVAL, absl::StrFormat("Not enough tokens, got %d, content: <<%s>>",
    59                                    fields.size(), content));
    60      }
    61  
    62      entry.spec = fields[0];
    63      entry.mount_point = fields[1];
    64      entry.fstype = fields[2];
    65      entry.mount_opts = fields[3];
    66      ASSIGN_OR_RETURN_ERRNO(entry.dump, Atoi<uint32_t>(fields[4]));
    67      ASSIGN_OR_RETURN_ERRNO(entry.fsck, Atoi<uint32_t>(fields[5]));
    68  
    69      entries.push_back(entry);
    70    }
    71    std::cerr << "<end of /proc/self/mounts>" << std::endl;
    72  
    73    return entries;
    74  }
    75  
    76  PosixErrorOr<std::vector<ProcMountInfoEntry>> ProcSelfMountInfoEntries() {
    77    std::string content;
    78    RETURN_IF_ERRNO(GetContents("/proc/self/mountinfo", &content));
    79    return ProcSelfMountInfoEntriesFrom(content);
    80  }
    81  
    82  PosixErrorOr<std::vector<ProcMountInfoEntry>> ProcSelfMountInfoEntriesFrom(
    83      const std::string& content) {
    84    std::vector<ProcMountInfoEntry> entries;
    85    std::vector<std::string> lines =
    86        absl::StrSplit(content, absl::ByChar('\n'), absl::AllowEmpty());
    87    std::cerr << "<contents of /proc/self/mountinfo>" << std::endl;
    88    for (const std::string& line : lines) {
    89      std::cerr << line << std::endl;
    90      if (line.empty()) {
    91        continue;
    92      }
    93  
    94      // Parse a single entry from /proc/self/mountinfo.
    95      //
    96      // Example entries:
    97      //
    98      // 22 28 0:20 / /sys rw,relatime shared:7 - sysfs sysfs rw
    99      // 23 28 0:21 / /proc rw,relatime shared:14 - proc proc rw
   100      // ^  ^    ^  ^   ^        ^         ^      ^  ^    ^   ^
   101      // 0  1    2  3   4        5         6      7  8    9   10
   102  
   103      ProcMountInfoEntry entry;
   104      std::vector<std::string> fields =
   105          absl::StrSplit(line, absl::ByChar(' '), absl::AllowEmpty());
   106      if (fields.size() < 10 || fields.size() > 11) {
   107        return PosixError(
   108            EINVAL, absl::StrFormat(
   109                        "Unexpected number of tokens, got %d, content: <<%s>>",
   110                        fields.size(), content));
   111      }
   112  
   113      ASSIGN_OR_RETURN_ERRNO(entry.id, Atoi<uint64_t>(fields[0]));
   114      ASSIGN_OR_RETURN_ERRNO(entry.parent_id, Atoi<uint64_t>(fields[1]));
   115  
   116      std::vector<std::string> devs =
   117          absl::StrSplit(fields[2], absl::ByChar(':'));
   118      if (devs.size() != 2) {
   119        return PosixError(
   120            EINVAL,
   121            absl::StrFormat(
   122                "Failed to parse dev number field %s: too many tokens, got %d",
   123                fields[2], devs.size()));
   124      }
   125      ASSIGN_OR_RETURN_ERRNO(entry.major, Atoi<dev_t>(devs[0]));
   126      ASSIGN_OR_RETURN_ERRNO(entry.minor, Atoi<dev_t>(devs[1]));
   127  
   128      entry.root = fields[3];
   129      entry.mount_point = fields[4];
   130      entry.mount_opts = fields[5];
   131  
   132      // The optional field (fields[6]) may or may not be present. We know based
   133      // on the total number of tokens.
   134      int off = -1;
   135      if (fields.size() == 11) {
   136        entry.optional = fields[6];
   137        off = 0;
   138      }
   139      // Field 7 is the optional field terminator char '-'.
   140      entry.fstype = fields[8 + off];
   141      entry.mount_source = fields[9 + off];
   142      entry.super_opts = fields[10 + off];
   143  
   144      entries.push_back(entry);
   145    }
   146    std::cerr << "<end of /proc/self/mountinfo>" << std::endl;
   147  
   148    return entries;
   149  }
   150  
   151  absl::flat_hash_map<std::string, std::string> ParseMountOptions(
   152      std::string mopts) {
   153    absl::flat_hash_map<std::string, std::string> entries;
   154    const std::vector<std::string> tokens =
   155        absl::StrSplit(mopts, absl::ByChar(','), absl::AllowEmpty());
   156    for (const auto& token : tokens) {
   157      std::vector<std::string> kv =
   158          absl::StrSplit(token, absl::MaxSplits('=', 1));
   159      if (kv.size() == 2) {
   160        entries[kv[0]] = kv[1];
   161      } else if (kv.size() == 1) {
   162        entries[kv[0]] = "";
   163      } else {
   164        TEST_CHECK_MSG(
   165            false,
   166            absl::StrFormat(
   167                "Invalid mount option token '%s', was split into %d subtokens",
   168                token, kv.size())
   169                .c_str());
   170      }
   171    }
   172    return entries;
   173  }
   174  
   175  }  // namespace testing
   176  }  // namespace gvisor