Mike Frysinger | 38ae98d | 2012-04-11 12:03:44 -0400 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Alex Deymo | 4527b9c | 2014-11-10 19:55:35 -0800 | [diff] [blame] | 5 | #include "cros-disks/system_mounter.h" |
| 6 | |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 7 | #include <sys/mount.h> |
| 8 | |
Ben Chan | 97f99ff | 2013-02-14 14:59:33 -0800 | [diff] [blame] | 9 | #include <base/files/scoped_temp_dir.h> |
Sergei Datsenko | 3d71d79 | 2020-11-17 21:46:22 +1100 | [diff] [blame] | 10 | #include <gmock/gmock.h> |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 11 | #include <gtest/gtest.h> |
| 12 | |
Ben Chan | 5ccd9fe | 2013-11-13 18:28:27 -0800 | [diff] [blame] | 13 | #include "cros-disks/mount_options.h" |
Sergei Datsenko | 85a1833 | 2019-04-08 14:25:03 +1000 | [diff] [blame] | 14 | #include "cros-disks/mount_point.h" |
Sam McNally | c56ae31 | 2018-05-22 13:14:27 +1000 | [diff] [blame] | 15 | #include "cros-disks/platform.h" |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 16 | |
| 17 | namespace cros_disks { |
| 18 | |
Sergei Datsenko | 3d71d79 | 2020-11-17 21:46:22 +1100 | [diff] [blame] | 19 | using testing::_; |
| 20 | using testing::Return; |
| 21 | |
Sergei Datsenko | e8faba5 | 2020-10-06 21:45:22 +1100 | [diff] [blame] | 22 | namespace { |
Sergei Datsenko | 3d71d79 | 2020-11-17 21:46:22 +1100 | [diff] [blame] | 23 | |
| 24 | constexpr uint64_t kDefaultMountFlags = |
| 25 | MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC | MS_NOSYMFOLLOW; |
| 26 | |
Sergei Datsenko | e8faba5 | 2020-10-06 21:45:22 +1100 | [diff] [blame] | 27 | class PlatformForTest : public Platform { |
| 28 | public: |
| 29 | // Tests are being run on devices that don't support nosymfollow. Strip it. |
| 30 | MountErrorType Mount(const std::string& source, |
| 31 | const std::string& target, |
| 32 | const std::string& filesystem_type, |
| 33 | uint64_t flags, |
| 34 | const std::string& options) const override { |
| 35 | EXPECT_TRUE((flags & MS_NOSYMFOLLOW) == MS_NOSYMFOLLOW); |
| 36 | return Platform::Mount(source, target, filesystem_type, |
| 37 | flags & ~MS_NOSYMFOLLOW, options); |
| 38 | } |
| 39 | }; |
Sergei Datsenko | 3d71d79 | 2020-11-17 21:46:22 +1100 | [diff] [blame] | 40 | |
| 41 | class MockPlatform : public Platform { |
| 42 | public: |
| 43 | MOCK_METHOD(MountErrorType, |
| 44 | Mount, |
| 45 | (const std::string& source, |
| 46 | const std::string& target, |
| 47 | const std::string& filesystem_type, |
| 48 | uint64_t flags, |
| 49 | const std::string& options), |
| 50 | (const, override)); |
| 51 | MOCK_METHOD(MountErrorType, |
| 52 | Unmount, |
| 53 | (const std::string& target, int flags), |
| 54 | (const, override)); |
| 55 | }; |
Sergei Datsenko | e8faba5 | 2020-10-06 21:45:22 +1100 | [diff] [blame] | 56 | } // namespace |
| 57 | |
Ben Chan | 33aa2bb | 2017-03-08 11:21:16 -0800 | [diff] [blame] | 58 | TEST(SystemMounterTest, RunAsRootMount) { |
Sergei Datsenko | e8faba5 | 2020-10-06 21:45:22 +1100 | [diff] [blame] | 59 | PlatformForTest platform; |
Sergei Datsenko | 3d71d79 | 2020-11-17 21:46:22 +1100 | [diff] [blame] | 60 | SystemMounter mounter(&platform, "tmpfs", /* read_only= */ false, {}); |
Sergei Datsenko | 85a1833 | 2019-04-08 14:25:03 +1000 | [diff] [blame] | 61 | |
Ben Chan | 97f99ff | 2013-02-14 14:59:33 -0800 | [diff] [blame] | 62 | base::ScopedTempDir temp_dir; |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 63 | ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
Sergei Datsenko | 85a1833 | 2019-04-08 14:25:03 +1000 | [diff] [blame] | 64 | |
François Degros | 3a79690 | 2019-07-12 14:49:58 +1000 | [diff] [blame] | 65 | MountErrorType error = MOUNT_ERROR_NONE; |
Sergei Datsenko | 85a1833 | 2019-04-08 14:25:03 +1000 | [diff] [blame] | 66 | auto mountpoint = mounter.Mount("/dev/null", temp_dir.GetPath(), {}, &error); |
| 67 | EXPECT_TRUE(mountpoint); |
François Degros | 3a79690 | 2019-07-12 14:49:58 +1000 | [diff] [blame] | 68 | EXPECT_EQ(MOUNT_ERROR_NONE, error); |
Sergei Datsenko | 85a1833 | 2019-04-08 14:25:03 +1000 | [diff] [blame] | 69 | error = mountpoint->Unmount(); |
François Degros | 3a79690 | 2019-07-12 14:49:58 +1000 | [diff] [blame] | 70 | EXPECT_EQ(MOUNT_ERROR_NONE, error); |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 71 | } |
| 72 | |
Ben Chan | 33aa2bb | 2017-03-08 11:21:16 -0800 | [diff] [blame] | 73 | TEST(SystemMounterTest, RunAsRootMountWithNonexistentSourcePath) { |
Sergei Datsenko | e8faba5 | 2020-10-06 21:45:22 +1100 | [diff] [blame] | 74 | PlatformForTest platform; |
Sergei Datsenko | 3d71d79 | 2020-11-17 21:46:22 +1100 | [diff] [blame] | 75 | SystemMounter mounter(&platform, "ext2", /* read_only= */ false, {}); |
Sergei Datsenko | 85a1833 | 2019-04-08 14:25:03 +1000 | [diff] [blame] | 76 | |
Ben Chan | 97f99ff | 2013-02-14 14:59:33 -0800 | [diff] [blame] | 77 | base::ScopedTempDir temp_dir; |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 78 | ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
Sergei Datsenko | 85a1833 | 2019-04-08 14:25:03 +1000 | [diff] [blame] | 79 | |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 80 | // To test mounting a nonexistent source path, use ext2 as the |
| 81 | // filesystem type instead of tmpfs since tmpfs does not care |
| 82 | // about source path. |
François Degros | 3a79690 | 2019-07-12 14:49:58 +1000 | [diff] [blame] | 83 | MountErrorType error = MOUNT_ERROR_NONE; |
Sergei Datsenko | 85a1833 | 2019-04-08 14:25:03 +1000 | [diff] [blame] | 84 | auto mountpoint = |
| 85 | mounter.Mount("/nonexistent", temp_dir.GetPath(), {}, &error); |
| 86 | EXPECT_FALSE(mountpoint); |
François Degros | 3a79690 | 2019-07-12 14:49:58 +1000 | [diff] [blame] | 87 | EXPECT_EQ(MOUNT_ERROR_INVALID_PATH, error); |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 88 | } |
| 89 | |
Ben Chan | 33aa2bb | 2017-03-08 11:21:16 -0800 | [diff] [blame] | 90 | TEST(SystemMounterTest, RunAsRootMountWithNonexistentTargetPath) { |
Sergei Datsenko | e8faba5 | 2020-10-06 21:45:22 +1100 | [diff] [blame] | 91 | PlatformForTest platform; |
Sergei Datsenko | 3d71d79 | 2020-11-17 21:46:22 +1100 | [diff] [blame] | 92 | SystemMounter mounter(&platform, "tmpfs", /* read_only= */ false, {}); |
Sergei Datsenko | 85a1833 | 2019-04-08 14:25:03 +1000 | [diff] [blame] | 93 | |
François Degros | 3a79690 | 2019-07-12 14:49:58 +1000 | [diff] [blame] | 94 | MountErrorType error = MOUNT_ERROR_NONE; |
Sergei Datsenko | 85a1833 | 2019-04-08 14:25:03 +1000 | [diff] [blame] | 95 | auto mountpoint = |
| 96 | mounter.Mount("/dev/null", base::FilePath("/nonexistent"), {}, &error); |
| 97 | EXPECT_FALSE(mountpoint); |
François Degros | 3a79690 | 2019-07-12 14:49:58 +1000 | [diff] [blame] | 98 | EXPECT_EQ(MOUNT_ERROR_INVALID_PATH, error); |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 99 | } |
| 100 | |
Ben Chan | 33aa2bb | 2017-03-08 11:21:16 -0800 | [diff] [blame] | 101 | TEST(SystemMounterTest, RunAsRootMountWithNonexistentFilesystemType) { |
Sergei Datsenko | e8faba5 | 2020-10-06 21:45:22 +1100 | [diff] [blame] | 102 | PlatformForTest platform; |
Sergei Datsenko | 3d71d79 | 2020-11-17 21:46:22 +1100 | [diff] [blame] | 103 | SystemMounter mounter(&platform, "nonexistentfs", /* read_only= */ false, {}); |
Sergei Datsenko | 85a1833 | 2019-04-08 14:25:03 +1000 | [diff] [blame] | 104 | |
Ben Chan | 97f99ff | 2013-02-14 14:59:33 -0800 | [diff] [blame] | 105 | base::ScopedTempDir temp_dir; |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 106 | ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
François Degros | 3a79690 | 2019-07-12 14:49:58 +1000 | [diff] [blame] | 107 | MountErrorType error = MOUNT_ERROR_NONE; |
Sergei Datsenko | 85a1833 | 2019-04-08 14:25:03 +1000 | [diff] [blame] | 108 | auto mountpoint = mounter.Mount("/dev/null", temp_dir.GetPath(), {}, &error); |
| 109 | EXPECT_FALSE(mountpoint); |
François Degros | 3a79690 | 2019-07-12 14:49:58 +1000 | [diff] [blame] | 110 | EXPECT_EQ(MOUNT_ERROR_UNSUPPORTED_FILESYSTEM, error); |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 111 | } |
| 112 | |
Sergei Datsenko | 3d71d79 | 2020-11-17 21:46:22 +1100 | [diff] [blame] | 113 | TEST(SystemMounterTest, MountFilesystem) { |
| 114 | MockPlatform platform; |
| 115 | SystemMounter mounter(&platform, "fstype", /* read_only= */ false, {}); |
| 116 | |
| 117 | EXPECT_CALL(platform, Mount("/dev/block", "/mnt/dir", "fstype", _, _)) |
| 118 | .WillOnce(Return(MOUNT_ERROR_NONE)); |
| 119 | MountErrorType error = MOUNT_ERROR_UNKNOWN; |
| 120 | auto mountpoint = |
| 121 | mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), {}, &error); |
| 122 | ASSERT_TRUE(mountpoint); |
| 123 | EXPECT_EQ(MOUNT_ERROR_NONE, error); |
| 124 | |
| 125 | EXPECT_CALL(platform, Unmount("/mnt/dir", 0)) |
| 126 | .WillOnce(Return(MOUNT_ERROR_NONE)); |
| 127 | mountpoint.reset(); |
| 128 | } |
| 129 | |
| 130 | TEST(SystemMounterTest, MountFailed) { |
| 131 | MockPlatform platform; |
| 132 | SystemMounter mounter(&platform, "fstype", /* read_only= */ false, {}); |
| 133 | |
| 134 | EXPECT_CALL(platform, Mount("/dev/block", "/mnt/dir", "fstype", _, _)) |
| 135 | .WillOnce(Return(MOUNT_ERROR_PATH_NOT_MOUNTED)); |
| 136 | EXPECT_CALL(platform, Unmount).Times(0); |
| 137 | |
| 138 | MountErrorType error = MOUNT_ERROR_UNKNOWN; |
| 139 | auto mountpoint = |
| 140 | mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), {}, &error); |
| 141 | ASSERT_FALSE(mountpoint); |
| 142 | EXPECT_EQ(MOUNT_ERROR_PATH_NOT_MOUNTED, error); |
| 143 | } |
| 144 | |
| 145 | TEST(SystemMounterTest, UnmountFailedNoRetry) { |
| 146 | MockPlatform platform; |
| 147 | SystemMounter mounter(&platform, "fstype", /* read_only= */ false, {}); |
| 148 | |
| 149 | EXPECT_CALL(platform, Mount(_, "/mnt/dir", "fstype", _, _)) |
| 150 | .WillOnce(Return(MOUNT_ERROR_NONE)); |
| 151 | MountErrorType error = MOUNT_ERROR_UNKNOWN; |
| 152 | auto mountpoint = |
| 153 | mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), {}, &error); |
| 154 | |
| 155 | EXPECT_CALL(platform, Unmount("/mnt/dir", 0)) |
| 156 | .WillOnce(Return(MOUNT_ERROR_INVALID_ARGUMENT)) |
| 157 | .WillOnce(Return(MOUNT_ERROR_NONE)); |
| 158 | EXPECT_EQ(MOUNT_ERROR_INVALID_ARGUMENT, mountpoint->Unmount()); |
| 159 | mountpoint.reset(); |
| 160 | } |
| 161 | |
| 162 | TEST(SystemMounterTest, UnmountBusyRetry) { |
| 163 | MockPlatform platform; |
| 164 | SystemMounter mounter(&platform, "fstype", /* read_only= */ false, {}); |
| 165 | |
| 166 | EXPECT_CALL(platform, Mount(_, "/mnt/dir", "fstype", _, _)) |
| 167 | .WillOnce(Return(MOUNT_ERROR_NONE)); |
| 168 | MountErrorType error = MOUNT_ERROR_UNKNOWN; |
| 169 | auto mountpoint = |
| 170 | mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), {}, &error); |
| 171 | |
| 172 | EXPECT_CALL(platform, Unmount("/mnt/dir", 0)) |
| 173 | .WillOnce(Return(MOUNT_ERROR_PATH_ALREADY_MOUNTED)); |
Sergei Datsenko | 0ba1203 | 2021-01-07 08:51:14 +1100 | [diff] [blame] | 174 | EXPECT_CALL(platform, Unmount("/mnt/dir", MNT_DETACH | MNT_FORCE)) |
Sergei Datsenko | 3d71d79 | 2020-11-17 21:46:22 +1100 | [diff] [blame] | 175 | .WillOnce(Return(MOUNT_ERROR_NONE)); |
| 176 | EXPECT_EQ(MOUNT_ERROR_NONE, mountpoint->Unmount()); |
| 177 | mountpoint.reset(); |
| 178 | } |
| 179 | |
| 180 | TEST(SystemMounterTest, MountFlags) { |
| 181 | MockPlatform platform; |
| 182 | SystemMounter mounter(&platform, "fstype", /* read_only= */ false, {}); |
| 183 | |
| 184 | EXPECT_CALL(platform, Mount(_, "/mnt/dir", "fstype", kDefaultMountFlags, _)) |
| 185 | .WillOnce(Return(MOUNT_ERROR_NONE)); |
| 186 | MountErrorType error = MOUNT_ERROR_UNKNOWN; |
| 187 | auto mountpoint = |
| 188 | mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), {}, &error); |
| 189 | } |
| 190 | |
| 191 | TEST(SystemMounterTest, ReadOnlyForced) { |
| 192 | MockPlatform platform; |
| 193 | SystemMounter mounter(&platform, "fstype", /* read_only= */ true, {}); |
| 194 | |
| 195 | EXPECT_CALL(platform, |
| 196 | Mount(_, "/mnt/dir", "fstype", kDefaultMountFlags | MS_RDONLY, _)) |
| 197 | .WillOnce(Return(MOUNT_ERROR_NONE)); |
| 198 | MountErrorType error = MOUNT_ERROR_UNKNOWN; |
| 199 | auto mountpoint = |
| 200 | mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), {}, &error); |
| 201 | } |
| 202 | |
| 203 | TEST(SystemMounterTest, ReadOnlyRequested) { |
| 204 | MockPlatform platform; |
| 205 | SystemMounter mounter(&platform, "fstype", /* read_only= */ false, {}); |
| 206 | |
| 207 | EXPECT_CALL(platform, |
| 208 | Mount(_, "/mnt/dir", "fstype", kDefaultMountFlags | MS_RDONLY, _)) |
| 209 | .WillOnce(Return(MOUNT_ERROR_NONE)); |
| 210 | MountErrorType error = MOUNT_ERROR_UNKNOWN; |
| 211 | auto mountpoint = |
| 212 | mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), {"ro"}, &error); |
| 213 | } |
| 214 | |
| 215 | TEST(SystemMounterTest, MountOptionsPassedButParamsIgnored) { |
| 216 | MockPlatform platform; |
| 217 | SystemMounter mounter(&platform, "fstype", /* read_only= */ false, |
| 218 | {"foo", "bar=baz"}); |
| 219 | |
| 220 | EXPECT_CALL(platform, Mount(_, "/mnt/dir", "fstype", _, "foo,bar=baz")) |
| 221 | .WillOnce(Return(MOUNT_ERROR_NONE)); |
| 222 | MountErrorType error = MOUNT_ERROR_UNKNOWN; |
| 223 | auto mountpoint = mounter.Mount("/dev/block", base::FilePath("/mnt/dir"), |
| 224 | {"abc=def", "xyz"}, &error); |
| 225 | } |
| 226 | |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 227 | } // namespace cros_disks |