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