From 36fdc6c9ef565be34f5ab27affed31eb2430e89a Mon Sep 17 00:00:00 2001 From: Adin Scannell Date: Mon, 26 Apr 2021 16:25:26 -0700 Subject: [PATCH] Handle tmpfs with 5 fields in /proc/mounts parsing. PiperOrigin-RevId: 370565903 --- test/syscalls/linux/mount.cc | 7 ++++-- test/util/BUILD | 12 +++++++++ test/util/mount_util.cc | 32 +++++++++++++++--------- test/util/mount_util.h | 10 ++++++++ test/util/mount_util_test.cc | 47 ++++++++++++++++++++++++++++++++++++ 5 files changed, 95 insertions(+), 13 deletions(-) create mode 100644 test/util/mount_util_test.cc diff --git a/test/syscalls/linux/mount.cc b/test/syscalls/linux/mount.cc index 92d0fb05c..3c7311782 100644 --- a/test/syscalls/linux/mount.cc +++ b/test/syscalls/linux/mount.cc @@ -45,6 +45,7 @@ namespace testing { namespace { +using ::testing::AnyOf; using ::testing::Contains; using ::testing::Pair; @@ -360,7 +361,8 @@ TEST(MountTest, MountInfo) { if (e.mount_point == dir.path()) { EXPECT_EQ(e.fstype, "tmpfs"); auto mopts = ParseMountOptions(e.mount_opts); - EXPECT_THAT(mopts, Contains(Pair("mode", "0123"))); + EXPECT_THAT(mopts, AnyOf(Contains(Pair("mode", "0123")), + Contains(Pair("mode", "123")))); } } @@ -371,7 +373,8 @@ TEST(MountTest, MountInfo) { if (e.mount_point == dir.path()) { EXPECT_EQ(e.fstype, "tmpfs"); auto mopts = ParseMountOptions(e.super_opts); - EXPECT_THAT(mopts, Contains(Pair("mode", "0123"))); + EXPECT_THAT(mopts, AnyOf(Contains(Pair("mode", "0123")), + Contains(Pair("mode", "123")))); } } } diff --git a/test/util/BUILD b/test/util/BUILD index 6feda0e26..8985b54af 100644 --- a/test/util/BUILD +++ b/test/util/BUILD @@ -149,6 +149,18 @@ cc_library( ], ) +cc_test( + name = "mount_util_test", + size = "small", + srcs = ["mount_util_test.cc"], + deps = [ + ":mount_util", + ":test_main", + ":test_util", + gtest, + ], +) + cc_library( name = "save_util", testonly = 1, diff --git a/test/util/mount_util.cc b/test/util/mount_util.cc index a79ce6420..48640d6a1 100644 --- a/test/util/mount_util.cc +++ b/test/util/mount_util.cc @@ -26,9 +26,14 @@ namespace testing { PosixErrorOr> ProcSelfMountsEntries() { std::string content; RETURN_IF_ERRNO(GetContents("/proc/self/mounts", &content)); + return ProcSelfMountsEntriesFrom(content); +} +PosixErrorOr> ProcSelfMountsEntriesFrom( + const std::string& content) { std::vector entries; - std::vector lines = absl::StrSplit(content, '\n'); + std::vector lines = + absl::StrSplit(content, absl::ByChar('\n'), absl::AllowEmpty()); std::cerr << "" << std::endl; for (const std::string& line : lines) { std::cerr << line << std::endl; @@ -47,11 +52,11 @@ PosixErrorOr> ProcSelfMountsEntries() { ProcMountsEntry entry; std::vector fields = - absl::StrSplit(line, absl::ByChar(' '), absl::SkipEmpty()); + absl::StrSplit(line, absl::ByChar(' '), absl::AllowEmpty()); if (fields.size() != 6) { - return PosixError(EINVAL, - absl::StrFormat("Not enough tokens, got %d, line: %s", - fields.size(), line)); + return PosixError( + EINVAL, absl::StrFormat("Not enough tokens, got %d, content: <<%s>>", + fields.size(), content)); } entry.spec = fields[0]; @@ -71,9 +76,14 @@ PosixErrorOr> ProcSelfMountsEntries() { PosixErrorOr> ProcSelfMountInfoEntries() { std::string content; RETURN_IF_ERRNO(GetContents("/proc/self/mountinfo", &content)); + return ProcSelfMountInfoEntriesFrom(content); +} +PosixErrorOr> ProcSelfMountInfoEntriesFrom( + const std::string& content) { std::vector entries; - std::vector lines = absl::StrSplit(content, '\n'); + std::vector lines = + absl::StrSplit(content, absl::ByChar('\n'), absl::AllowEmpty()); std::cerr << "" << std::endl; for (const std::string& line : lines) { std::cerr << line << std::endl; @@ -92,12 +102,12 @@ PosixErrorOr> ProcSelfMountInfoEntries() { ProcMountInfoEntry entry; std::vector fields = - absl::StrSplit(line, absl::ByChar(' '), absl::SkipEmpty()); + absl::StrSplit(line, absl::ByChar(' '), absl::AllowEmpty()); if (fields.size() < 10 || fields.size() > 11) { return PosixError( - EINVAL, - absl::StrFormat("Unexpected number of tokens, got %d, line: %s", - fields.size(), line)); + EINVAL, absl::StrFormat( + "Unexpected number of tokens, got %d, content: <<%s>>", + fields.size(), content)); } ASSIGN_OR_RETURN_ERRNO(entry.id, Atoi(fields[0])); @@ -142,7 +152,7 @@ absl::flat_hash_map ParseMountOptions( std::string mopts) { absl::flat_hash_map entries; const std::vector tokens = - absl::StrSplit(mopts, absl::ByChar(','), absl::SkipEmpty()); + absl::StrSplit(mopts, absl::ByChar(','), absl::AllowEmpty()); for (const auto& token : tokens) { std::vector kv = absl::StrSplit(token, absl::MaxSplits('=', 1)); diff --git a/test/util/mount_util.h b/test/util/mount_util.h index b75a490fb..3f8a1c0f1 100644 --- a/test/util/mount_util.h +++ b/test/util/mount_util.h @@ -58,6 +58,11 @@ struct ProcMountsEntry { // ProcSelfMountsEntries returns a parsed representation of /proc/self/mounts. PosixErrorOr> ProcSelfMountsEntries(); +// ProcSelfMountsEntries returns a parsed representation of mounts from the +// provided content. +PosixErrorOr> ProcSelfMountsEntriesFrom( + const std::string& content); + struct ProcMountInfoEntry { uint64_t id; uint64_t parent_id; @@ -76,6 +81,11 @@ struct ProcMountInfoEntry { // /proc/self/mountinfo. PosixErrorOr> ProcSelfMountInfoEntries(); +// ProcSelfMountInfoEntriesFrom returns a parsed representation of +// mountinfo from the provided content. +PosixErrorOr> ProcSelfMountInfoEntriesFrom( + const std::string&); + // Interprets the input string mopts as a comma separated list of mount // options. A mount option can either be just a value, or a key=value pair. For // example, the string "rw,relatime,fd=7" will be parsed into a map like { "rw": diff --git a/test/util/mount_util_test.cc b/test/util/mount_util_test.cc new file mode 100644 index 000000000..2bcb6cc43 --- /dev/null +++ b/test/util/mount_util_test.cc @@ -0,0 +1,47 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/util/mount_util.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(ParseMounts, Mounts) { + auto entries = ASSERT_NO_ERRNO_AND_VALUE(ProcSelfMountsEntriesFrom( + R"proc(sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0 +proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0 + /mnt tmpfs rw,noexec 0 0 +)proc")); + EXPECT_EQ(entries.size(), 3); +} + +TEST(ParseMounts, MountInfo) { + auto entries = ASSERT_NO_ERRNO_AND_VALUE(ProcSelfMountInfoEntriesFrom( + R"proc(22 28 0:20 / /sys rw,relatime shared:7 - sysfs sysfs rw +23 28 0:21 / /proc rw,relatime shared:14 - proc proc rw +2007 8844 0:278 / /mnt rw,noexec - tmpfs rw,mode=123,uid=268601820,gid=5000 +)proc")); + EXPECT_EQ(entries.size(), 3); +} + +} // namespace + +} // namespace testing +} // namespace gvisor