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