Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 1 | // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "cros-disks/drivefs_helper.h" |
| 6 | |
| 7 | #include <sys/mount.h> |
| 8 | |
Qijiang Fan | 713061e | 2021-03-08 15:45:12 +0900 | [diff] [blame] | 9 | #include <base/check_op.h> |
Sam McNally | f6b4ef8 | 2019-06-12 14:22:08 +1000 | [diff] [blame] | 10 | #include <base/files/file_util.h> |
| 11 | #include <base/files/scoped_temp_dir.h> |
Qijiang Fan | 886c469 | 2021-02-19 11:54:10 +0900 | [diff] [blame] | 12 | #include <base/notreached.h> |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 13 | #include <base/strings/string_split.h> |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 14 | #include <base/strings/string_util.h> |
Simon Glass | 2b1da09 | 2020-05-21 12:24:16 -0600 | [diff] [blame] | 15 | #include <brillo/process/process_reaper.h> |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 16 | #include <gmock/gmock.h> |
| 17 | #include <gtest/gtest.h> |
| 18 | |
| 19 | #include "cros-disks/fuse_mounter.h" |
| 20 | #include "cros-disks/mount_options.h" |
| 21 | #include "cros-disks/platform.h" |
| 22 | #include "cros-disks/uri.h" |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 23 | #include "cros-disks/user.h" |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 24 | |
| 25 | namespace cros_disks { |
| 26 | namespace { |
| 27 | |
| 28 | using testing::_; |
| 29 | using testing::DoAll; |
Sergei Datsenko | 562d4f0 | 2018-06-28 15:42:09 +1000 | [diff] [blame] | 30 | using testing::EndsWith; |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 31 | using testing::HasSubstr; |
| 32 | using testing::Invoke; |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 33 | using testing::IsSupersetOf; |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 34 | using testing::Return; |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 35 | using testing::SaveArg; |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 36 | using testing::SetArgPointee; |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 37 | using testing::StrEq; |
| 38 | using testing::UnorderedElementsAre; |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 39 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 40 | constexpr char kSource[] = "drivefs://id"; |
| 41 | constexpr char kDataDir[] = "/home/chronos/user/GCache/foo"; |
| 42 | constexpr char kMountPath[] = "/media/fuse/drivefs/id"; |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 43 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 44 | constexpr char kPrefixParam[] = "prefix=/media/fuse/drivefs/id"; |
| 45 | constexpr char kDataDirParam[] = "datadir=/home/chronos/user/GCache/foo"; |
| 46 | constexpr char kMyFilesParam[] = "myfiles=/home/chronos/user/MyFiles"; |
| 47 | |
| 48 | std::vector<std::string> ParseOptions(const SandboxedProcess& sandbox) { |
| 49 | CHECK_EQ(2, sandbox.arguments().size()); |
| 50 | CHECK_EQ("-o", sandbox.arguments()[0]); |
| 51 | return base::SplitString(sandbox.arguments()[1], ",", |
| 52 | base::WhitespaceHandling::KEEP_WHITESPACE, |
| 53 | base::SplitResult::SPLIT_WANT_ALL); |
| 54 | } |
Sam McNally | dd0ff98 | 2019-06-12 18:18:36 +1000 | [diff] [blame] | 55 | |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 56 | // Mock Platform implementation for testing. |
| 57 | class MockPlatform : public Platform { |
| 58 | public: |
| 59 | MockPlatform() { |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 60 | ON_CALL(*this, GetRealPath(_, _)) |
| 61 | .WillByDefault(Invoke(this, &MockPlatform::GetRealPathImpl)); |
Sam McNally | 0b2361b | 2018-05-25 10:17:19 +1000 | [diff] [blame] | 62 | ON_CALL(*this, DirectoryExists(_)).WillByDefault(Return(true)); |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 63 | ON_CALL(*this, GetOwnership(_, _, _)) |
| 64 | .WillByDefault(DoAll(SetArgPointee<1>(kChronosUID), Return(true))); |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 65 | } |
| 66 | |
Ben Chan | ef8e603 | 2019-09-27 08:24:56 -0700 | [diff] [blame] | 67 | MOCK_METHOD(bool, |
| 68 | GetRealPath, |
| 69 | (const std::string&, std::string*), |
| 70 | (const, override)); |
Ben Chan | ef8e603 | 2019-09-27 08:24:56 -0700 | [diff] [blame] | 71 | MOCK_METHOD(bool, PathExists, (const std::string&), (const, override)); |
| 72 | MOCK_METHOD(bool, DirectoryExists, (const std::string&), (const, override)); |
Ben Chan | ef8e603 | 2019-09-27 08:24:56 -0700 | [diff] [blame] | 73 | MOCK_METHOD(bool, CreateDirectory, (const std::string&), (const, override)); |
| 74 | MOCK_METHOD(bool, |
| 75 | RemoveEmptyDirectory, |
| 76 | (const std::string&), |
| 77 | (const, override)); |
| 78 | MOCK_METHOD(bool, |
Ben Chan | ef8e603 | 2019-09-27 08:24:56 -0700 | [diff] [blame] | 79 | GetOwnership, |
| 80 | (const std::string&, uid_t*, gid_t*), |
| 81 | (const, override)); |
| 82 | MOCK_METHOD(bool, |
| 83 | SetPermissions, |
| 84 | (const std::string&, mode_t), |
| 85 | (const, override)); |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 86 | |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 87 | bool GetRealPathImpl(const std::string& path, std::string* real_path) const { |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 88 | std::vector<std::string> components; |
| 89 | base::FilePath(path).GetComponents(&components); |
| 90 | base::FilePath result(components[0]); |
| 91 | components.erase(components.begin()); |
| 92 | for (const auto& part : components) { |
| 93 | if (part != ".") |
| 94 | result = result.Append(part); |
Sam McNally | dd0ff98 | 2019-06-12 18:18:36 +1000 | [diff] [blame] | 95 | } |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 96 | *real_path = result.value(); |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 97 | return true; |
| 98 | } |
| 99 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 100 | bool GetUserAndGroupId(const std::string& user, |
| 101 | uid_t* user_id, |
| 102 | gid_t* group_id) const override { |
| 103 | NOTREACHED(); |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 104 | return false; |
| 105 | } |
| 106 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 107 | bool GetGroupId(const std::string& group, gid_t* group_id) const override { |
| 108 | NOTREACHED(); |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 109 | return false; |
| 110 | } |
Sam McNally | f6b4ef8 | 2019-06-12 14:22:08 +1000 | [diff] [blame] | 111 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 112 | bool SetOwnership(const std::string&, uid_t, gid_t) const override { |
| 113 | NOTREACHED(); |
| 114 | return false; |
| 115 | } |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 116 | }; |
| 117 | |
| 118 | class TestDrivefsHelper : public DrivefsHelper { |
| 119 | public: |
Sergei Datsenko | a910bba | 2019-06-18 13:31:59 +1000 | [diff] [blame] | 120 | TestDrivefsHelper(const Platform* platform, |
| 121 | brillo::ProcessReaper* process_reaper) |
| 122 | : DrivefsHelper(platform, process_reaper) { |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 123 | } |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 124 | using DrivefsHelper::ConfigureSandbox; |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 125 | }; |
| 126 | |
| 127 | class DrivefsHelperTest : public ::testing::Test { |
| 128 | public: |
Sergei Datsenko | a910bba | 2019-06-18 13:31:59 +1000 | [diff] [blame] | 129 | DrivefsHelperTest() : helper_(&platform_, &process_reaper_) {} |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 130 | |
| 131 | protected: |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 132 | MockPlatform platform_; |
Sergei Datsenko | a910bba | 2019-06-18 13:31:59 +1000 | [diff] [blame] | 133 | brillo::ProcessReaper process_reaper_; |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 134 | TestDrivefsHelper helper_; |
| 135 | }; |
| 136 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 137 | TEST_F(DrivefsHelperTest, ConfigureSandbox) { |
| 138 | FakeSandboxedProcess sandbox; |
| 139 | auto error = helper_.ConfigureSandbox( |
| 140 | kSource, base::FilePath(kMountPath), |
| 141 | {"datadir=/home/chronos//user/GCache//foo/./"}, &sandbox); |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 142 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 143 | EXPECT_EQ(MOUNT_ERROR_NONE, error); |
| 144 | auto options = ParseOptions(sandbox); |
| 145 | EXPECT_THAT(options, |
| 146 | UnorderedElementsAre(kDataDirParam, "identity=id", "uid=1000", |
| 147 | "gid=1001", kPrefixParam)); |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 148 | } |
| 149 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 150 | TEST_F(DrivefsHelperTest, ConfigureSandboxWithMyFiles) { |
| 151 | FakeSandboxedProcess sandbox; |
| 152 | auto error = helper_.ConfigureSandbox( |
| 153 | kSource, base::FilePath(kMountPath), |
| 154 | {kDataDirParam, "myfiles=/home/chronos//user/.//MyFiles"}, &sandbox); |
Sam McNally | dd0ff98 | 2019-06-12 18:18:36 +1000 | [diff] [blame] | 155 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 156 | EXPECT_EQ(MOUNT_ERROR_NONE, error); |
| 157 | auto options = ParseOptions(sandbox); |
| 158 | EXPECT_THAT(options, IsSupersetOf({StrEq(kMyFilesParam)})); |
Sam McNally | dd0ff98 | 2019-06-12 18:18:36 +1000 | [diff] [blame] | 159 | } |
| 160 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 161 | TEST_F(DrivefsHelperTest, ConfigureSandboxFailsIfInvalidSource) { |
| 162 | FakeSandboxedProcess sandbox; |
| 163 | auto error = helper_.ConfigureSandbox( |
| 164 | "drive://id", base::FilePath(kMountPath), {kDataDirParam}, &sandbox); |
| 165 | EXPECT_NE(MOUNT_ERROR_NONE, error); |
Sam McNally | 0b2361b | 2018-05-25 10:17:19 +1000 | [diff] [blame] | 166 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 167 | error = helper_.ConfigureSandbox("/dev/block", base::FilePath(kMountPath), |
| 168 | {kDataDirParam}, &sandbox); |
| 169 | EXPECT_NE(MOUNT_ERROR_NONE, error); |
Sam McNally | 0b2361b | 2018-05-25 10:17:19 +1000 | [diff] [blame] | 170 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 171 | error = helper_.ConfigureSandbox("drivefs:/foo", base::FilePath(kMountPath), |
| 172 | {kDataDirParam}, &sandbox); |
| 173 | EXPECT_NE(MOUNT_ERROR_NONE, error); |
Sam McNally | 0b2361b | 2018-05-25 10:17:19 +1000 | [diff] [blame] | 174 | } |
| 175 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 176 | TEST_F(DrivefsHelperTest, ConfigureSandboxFailsIfDataDirInvalid) { |
| 177 | FakeSandboxedProcess sandbox; |
| 178 | auto error = helper_.ConfigureSandbox(kSource, base::FilePath(kMountPath), {}, |
| 179 | &sandbox); |
| 180 | EXPECT_NE(MOUNT_ERROR_NONE, error); |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 181 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 182 | error = helper_.ConfigureSandbox(kSource, base::FilePath(kMountPath), |
| 183 | {"datadir=dodgy/path"}, &sandbox); |
| 184 | EXPECT_NE(MOUNT_ERROR_NONE, error); |
| 185 | |
| 186 | error = helper_.ConfigureSandbox(kSource, base::FilePath(kMountPath), |
| 187 | {"datadir=/nonhome/dir"}, &sandbox); |
| 188 | EXPECT_NE(MOUNT_ERROR_NONE, error); |
| 189 | |
| 190 | error = helper_.ConfigureSandbox(kSource, base::FilePath(kMountPath), |
| 191 | {"datadir=/home/chronos/../../etc/passwd"}, |
| 192 | &sandbox); |
| 193 | EXPECT_NE(MOUNT_ERROR_NONE, error); |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 194 | } |
| 195 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 196 | TEST_F(DrivefsHelperTest, ConfigureSandboxFailsIfDataDirDoesntExist) { |
| 197 | EXPECT_CALL(platform_, DirectoryExists(kDataDir)).WillOnce(Return(false)); |
| 198 | FakeSandboxedProcess sandbox; |
| 199 | auto error = helper_.ConfigureSandbox(kSource, base::FilePath(kMountPath), |
| 200 | {kDataDirParam}, &sandbox); |
| 201 | EXPECT_NE(MOUNT_ERROR_NONE, error); |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 202 | } |
| 203 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 204 | TEST_F(DrivefsHelperTest, ConfigureSandboxFailsWhenCantStat) { |
| 205 | EXPECT_CALL(platform_, GetOwnership(kDataDir, _, _)).WillOnce(Return(false)); |
| 206 | FakeSandboxedProcess sandbox; |
| 207 | auto error = helper_.ConfigureSandbox(kSource, base::FilePath(kMountPath), |
| 208 | {kDataDirParam}, &sandbox); |
| 209 | EXPECT_NE(MOUNT_ERROR_NONE, error); |
Sam McNally | 0b2361b | 2018-05-25 10:17:19 +1000 | [diff] [blame] | 210 | } |
| 211 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 212 | TEST_F(DrivefsHelperTest, ConfigureSandboxFailsWhenWrongOwner) { |
| 213 | EXPECT_CALL(platform_, GetOwnership(kDataDir, _, _)) |
| 214 | .WillOnce(DoAll(SetArgPointee<1>(kChronosUID + 100), Return(false))); |
| 215 | FakeSandboxedProcess sandbox; |
| 216 | auto error = helper_.ConfigureSandbox(kSource, base::FilePath(kMountPath), |
| 217 | {kDataDirParam}, &sandbox); |
| 218 | EXPECT_NE(MOUNT_ERROR_NONE, error); |
Sam McNally | dd0ff98 | 2019-06-12 18:18:36 +1000 | [diff] [blame] | 219 | } |
| 220 | |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 221 | } // namespace |
| 222 | } // namespace cros_disks |