| 174 | |
| 175 | |
| 176 | Try<MountInfoTable> MountInfoTable::read( |
| 177 | const string& lines, |
| 178 | bool hierarchicalSort) |
| 179 | { |
| 180 | MountInfoTable table; |
| 181 | |
| 182 | foreach (const string& line, strings::tokenize(lines, "\n")) { |
| 183 | Try<Entry> parse = MountInfoTable::Entry::parse(line); |
| 184 | if (parse.isError()) { |
| 185 | return Error("Failed to parse entry '" + line + "': " + parse.error()); |
| 186 | } |
| 187 | |
| 188 | table.entries.push_back(parse.get()); |
| 189 | } |
| 190 | |
| 191 | // If `hierarchicalSort == true`, then sort the entries in |
| 192 | // the newly constructed table hierarchically. That is, sort |
| 193 | // them according to the invariant that all parent entries |
| 194 | // appear before their child entries. |
| 195 | if (hierarchicalSort) { |
| 196 | Option<int> rootParentId = None(); |
| 197 | |
| 198 | // Construct a representation of the mount hierarchy using a hashmap. |
| 199 | hashmap<int, vector<MountInfoTable::Entry>> parentToChildren; |
| 200 | |
| 201 | foreach (const MountInfoTable::Entry& entry, table.entries) { |
| 202 | if (entry.target == "/") { |
| 203 | CHECK_NONE(rootParentId); |
| 204 | rootParentId = entry.parent; |
| 205 | } |
| 206 | parentToChildren[entry.parent].push_back(entry); |
| 207 | } |
| 208 | |
| 209 | // Walk the hashmap and construct a list of entries sorted |
| 210 | // hierarchically. The recursion eventually terminates because |
| 211 | // entries in MountInfoTable are guaranteed to have no cycles. |
| 212 | // We double check though, just to make sure. |
| 213 | hashset<int> visitedParents; |
| 214 | vector<MountInfoTable::Entry> sortedEntries; |
| 215 | |
| 216 | std::function<void(int)> sortFrom = [&](int parentId) { |
| 217 | CHECK(!visitedParents.contains(parentId)) |
| 218 | << "Cycle found in mount table hierarchy at entry" |
| 219 | << " '" << stringify(parentId) << "': " << std::endl << lines; |
| 220 | |
| 221 | visitedParents.insert(parentId); |
| 222 | |
| 223 | foreach (const MountInfoTable::Entry& entry, parentToChildren[parentId]) { |
| 224 | sortedEntries.push_back(entry); |
| 225 | |
| 226 | // It is legal to have a `MountInfoTable` entry whose |
| 227 | // `entry.id` is the same as its `entry.parent`. This can |
| 228 | // happen (for example), if a system boots from the network |
| 229 | // and then keeps the original `/` in RAM. To avoid cycles |
| 230 | // when walking the mount hierarchy, we only recurse into our |
| 231 | // children if this case is not satisfied. |
| 232 | if (parentId != entry.id) { |
| 233 | sortFrom(entry.id); |
nothing calls this directly
no test coverage detected