blob: 09c0fee98714673470c9e8fa81631f0dd72756e9 [file] [log] [blame]
Sergei Datsenko6907a132019-04-01 11:26:56 +11001// Copyright 2019 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/fuse_mounter.h"
6
Anand K Mistrya24c75b2020-01-09 17:57:25 +11007#include <sys/mount.h>
8
Sergei Datsenko6907a132019-04-01 11:26:56 +11009#include <memory>
10#include <string>
Sergei Datsenko88035aa2020-11-15 00:24:01 +110011#include <utility>
Sergei Datsenko6907a132019-04-01 11:26:56 +110012#include <vector>
13
Sergei Datsenkoadd282e2020-11-16 15:41:24 +110014#include <base/files/file_util.h>
15#include <base/files/scoped_temp_dir.h>
Qijiang Fan886c4692021-02-19 11:54:10 +090016#include <base/notreached.h>
Sergei Datsenkoe2d1d0b2020-11-18 12:48:13 +110017#include <base/strings/string_util.h>
Sergei Datsenkoadd282e2020-11-16 15:41:24 +110018#include <base/strings/stringprintf.h>
Simon Glass2b1da092020-05-21 12:24:16 -060019#include <brillo/process/process_reaper.h>
Sergei Datsenko6907a132019-04-01 11:26:56 +110020#include <gmock/gmock.h>
21#include <gtest/gtest.h>
22
François Degros9eb10182020-07-13 17:08:50 +100023#include "cros-disks/error_logger.h"
Sergei Datsenko6907a132019-04-01 11:26:56 +110024#include "cros-disks/mount_options.h"
Anand K Mistry9f4611e2019-12-19 16:06:39 +110025#include "cros-disks/mount_point.h"
Sergei Datsenko6907a132019-04-01 11:26:56 +110026#include "cros-disks/platform.h"
27#include "cros-disks/sandboxed_process.h"
28
29namespace cros_disks {
30
31namespace {
32
33using testing::_;
Sergei Datsenko88035aa2020-11-15 00:24:01 +110034using testing::ByMove;
Sergei Datsenko199f4f42020-10-08 10:47:12 +110035using testing::DoAll;
Sergei Datsenko6907a132019-04-01 11:26:56 +110036using testing::ElementsAre;
Sergei Datsenko199f4f42020-10-08 10:47:12 +110037using testing::EndsWith;
Sergei Datsenko6907a132019-04-01 11:26:56 +110038using testing::Invoke;
François Degros5c6d9cb2020-07-16 13:44:44 +100039using testing::IsEmpty;
Sergei Datsenko6907a132019-04-01 11:26:56 +110040using testing::Return;
Sergei Datsenko199f4f42020-10-08 10:47:12 +110041using testing::SetArgPointee;
Sergei Datsenko6907a132019-04-01 11:26:56 +110042using testing::StartsWith;
43
44const uid_t kMountUID = 200;
45const gid_t kMountGID = 201;
Sergei Datsenko6907a132019-04-01 11:26:56 +110046const char kMountUser[] = "fuse-fuse";
Sergei Datsenkoe2d1d0b2020-11-18 12:48:13 +110047const char kFUSEType[] = "fusefs";
François Degrosac76b5a2019-12-19 15:34:06 +110048const char kSomeSource[] = "/dev/dummy";
Sergei Datsenko6907a132019-04-01 11:26:56 +110049const char kMountDir[] = "/mnt";
Derek Basehorea057a972021-01-19 18:45:58 -080050const char kCgroup[] = "/sys/fs/cgroup/freezer/exe/cgroup.procs";
Sergei Datsenko3928f782020-12-31 09:14:04 +110051const int kFUSEMountFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
Sergei Datsenko6907a132019-04-01 11:26:56 +110052
53// Mock Platform implementation for testing.
54class MockFUSEPlatform : public Platform {
55 public:
56 MockFUSEPlatform() {
57 ON_CALL(*this, GetUserAndGroupId(_, _, _))
58 .WillByDefault(Invoke(this, &MockFUSEPlatform::GetUserAndGroupIdImpl));
Sergei Datsenko6907a132019-04-01 11:26:56 +110059 ON_CALL(*this, PathExists(_)).WillByDefault(Return(true));
60 ON_CALL(*this, SetOwnership(_, _, _)).WillByDefault(Return(true));
61 ON_CALL(*this, SetPermissions(_, _)).WillByDefault(Return(true));
62 }
63
Ben Chanef8e6032019-09-27 08:24:56 -070064 MOCK_METHOD(bool,
65 GetUserAndGroupId,
66 (const std::string&, uid_t*, gid_t*),
67 (const, override));
Ben Chanef8e6032019-09-27 08:24:56 -070068 MOCK_METHOD(MountErrorType,
69 Mount,
70 (const std::string&,
71 const std::string&,
72 const std::string&,
73 uint64_t,
74 const std::string&),
75 (const, override));
76 MOCK_METHOD(MountErrorType,
77 Unmount,
78 (const std::string&, int),
79 (const, override));
80 MOCK_METHOD(bool, PathExists, (const std::string&), (const, override));
81 MOCK_METHOD(bool,
82 RemoveEmptyDirectory,
83 (const std::string&),
84 (const, override));
85 MOCK_METHOD(bool,
86 SetOwnership,
87 (const std::string&, uid_t, gid_t),
88 (const, override));
89 MOCK_METHOD(bool,
90 GetOwnership,
91 (const std::string&, uid_t*, gid_t*),
92 (const, override));
93 MOCK_METHOD(bool,
94 SetPermissions,
95 (const std::string&, mode_t),
96 (const, override));
Sergei Datsenko6907a132019-04-01 11:26:56 +110097
Sergei Datsenkoe2d1d0b2020-11-18 12:48:13 +110098 bool Lstat(const std::string& path,
99 base::stat_wrapper_t* out) const override {
100 if (base::StartsWith(path, "/dev/", base::CompareCase::SENSITIVE)) {
101 out->st_mode = S_IFBLK | 0640;
102 return true;
103 }
104 return false;
105 }
106
Sergei Datsenko6907a132019-04-01 11:26:56 +1100107 private:
108 bool GetUserAndGroupIdImpl(const std::string& user,
109 uid_t* user_id,
110 gid_t* group_id) const {
Sergei Datsenko6907a132019-04-01 11:26:56 +1100111 if (user == kMountUser) {
112 if (user_id)
113 *user_id = kMountUID;
114 if (group_id)
115 *group_id = kMountGID;
116 return true;
117 }
118 return false;
119 }
Sergei Datsenko6907a132019-04-01 11:26:56 +1100120};
121
122class MockSandboxedProcess : public SandboxedProcess {
123 public:
124 MockSandboxedProcess() = default;
Ben Chan21150af2019-09-11 17:04:07 -0700125 MOCK_METHOD(pid_t,
126 StartImpl,
François Degros1ef69942019-10-01 15:31:17 +1000127 (base::ScopedFD, base::ScopedFD, base::ScopedFD),
Ben Chan21150af2019-09-11 17:04:07 -0700128 (override));
129 MOCK_METHOD(int, WaitImpl, (), (override));
François Degros92bbea42019-09-13 10:42:52 +1000130 MOCK_METHOD(int, WaitNonBlockingImpl, (), (override));
Sergei Datsenko6907a132019-04-01 11:26:56 +1100131};
132
133class FUSEMounterForTesting : public FUSEMounter {
134 public:
Sergei Datsenkoa910bba2019-06-18 13:31:59 +1000135 FUSEMounterForTesting(const Platform* platform,
136 brillo::ProcessReaper* process_reaper)
Sergei Datsenko1d2cbf82020-12-08 21:54:42 +1100137 : FUSEMounter(platform, process_reaper, kFUSEType, {}) {}
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100138
Sergei Datsenko88035aa2020-11-15 00:24:01 +1100139 MOCK_METHOD(std::unique_ptr<SandboxedProcess>,
140 PrepareSandbox,
141 (const std::string& source,
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100142 const base::FilePath& target_path,
143 std::vector<std::string> params,
144 MountErrorType* error),
145 (const override));
146
147 bool CanMount(const std::string& source,
148 const std::vector<std::string>& params,
149 base::FilePath* suggested_dir_name) const override {
Sergei Datsenko88035aa2020-11-15 00:24:01 +1100150 NOTREACHED();
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100151 return true;
152 }
153};
154
155} // namespace
156
Sergei Datsenkoadd282e2020-11-16 15:41:24 +1100157class FUSESandboxedProcessFactoryTest : public ::testing::Test {
158 public:
159 FUSESandboxedProcessFactoryTest() {}
160
161 protected:
162 static bool ApplyConfiguration(const FUSESandboxedProcessFactory& factory,
163 SandboxedProcess* sandbox) {
164 return factory.ConfigureSandbox(sandbox);
165 }
166
167 MockFUSEPlatform platform_;
168 const base::FilePath exe_{"/bin/exe"};
169 const OwnerUser run_as_{123, 456};
170};
171
172TEST_F(FUSESandboxedProcessFactoryTest, BasicSetup) {
Derek Basehorea057a972021-01-19 18:45:58 -0800173 EXPECT_CALL(platform_, PathExists(kCgroup)).WillOnce(Return(true));
Sergei Datsenkoadd282e2020-11-16 15:41:24 +1100174 EXPECT_CALL(platform_, PathExists(exe_.value())).WillOnce(Return(true));
175 FUSESandboxedProcessFactory factory(&platform_, {exe_}, run_as_);
176 MockSandboxedProcess sandbox_;
177 EXPECT_TRUE(ApplyConfiguration(factory, &sandbox_));
178}
179
180TEST_F(FUSESandboxedProcessFactoryTest, BasicSetup_MissingExecutable) {
Derek Basehorea057a972021-01-19 18:45:58 -0800181 EXPECT_CALL(platform_, PathExists(kCgroup)).WillOnce(Return(true));
Sergei Datsenkoadd282e2020-11-16 15:41:24 +1100182 EXPECT_CALL(platform_, PathExists(exe_.value())).WillOnce(Return(false));
183 FUSESandboxedProcessFactory factory(&platform_, {exe_}, run_as_);
184 MockSandboxedProcess sandbox_;
185 EXPECT_FALSE(ApplyConfiguration(factory, &sandbox_));
186}
187
188// TODO(crbug.com/1149685): Disabled as seccomp crashes qemu used for ARM.
189TEST_F(FUSESandboxedProcessFactoryTest, DISABLED_SeccompPolicy) {
190 base::ScopedTempDir tmp;
191 ASSERT_TRUE(tmp.CreateUniqueTempDir());
192 base::FilePath seccomp = tmp.GetPath().Append("exe.policy");
193 std::string policy = "close: 1\n";
194 base::WriteFile(seccomp, policy.c_str(), policy.length());
195 EXPECT_CALL(platform_, PathExists(seccomp.value())).WillOnce(Return(true));
Derek Basehorea057a972021-01-19 18:45:58 -0800196 EXPECT_CALL(platform_, PathExists(kCgroup)).WillOnce(Return(true));
Sergei Datsenkoadd282e2020-11-16 15:41:24 +1100197 EXPECT_CALL(platform_, PathExists(exe_.value())).WillOnce(Return(true));
198 FUSESandboxedProcessFactory factory(&platform_, {exe_, seccomp}, run_as_);
199 MockSandboxedProcess sandbox_;
200 EXPECT_TRUE(ApplyConfiguration(factory, &sandbox_));
201}
202
203TEST_F(FUSESandboxedProcessFactoryTest, SeccompPolicy_MissingPolicy) {
204 base::ScopedTempDir tmp;
205 ASSERT_TRUE(tmp.CreateUniqueTempDir());
206 base::FilePath seccomp = tmp.GetPath().Append("exe.policy");
Derek Basehorea057a972021-01-19 18:45:58 -0800207 EXPECT_CALL(platform_, PathExists(kCgroup)).WillOnce(Return(true));
Sergei Datsenkoadd282e2020-11-16 15:41:24 +1100208 EXPECT_CALL(platform_, PathExists(seccomp.value())).WillOnce(Return(false));
209 FUSESandboxedProcessFactory factory(&platform_, {exe_, seccomp}, run_as_);
210 MockSandboxedProcess sandbox_;
211 EXPECT_FALSE(ApplyConfiguration(factory, &sandbox_));
212}
213
214TEST_F(FUSESandboxedProcessFactoryTest, NetworkEnabled_NonCrostini) {
Derek Basehorea057a972021-01-19 18:45:58 -0800215 EXPECT_CALL(platform_, PathExists(kCgroup)).WillOnce(Return(true));
Sergei Datsenkoadd282e2020-11-16 15:41:24 +1100216 EXPECT_CALL(platform_, PathExists(exe_.value())).WillOnce(Return(true));
217 EXPECT_CALL(platform_, PathExists("/etc/hosts.d")).WillOnce(Return(false));
218 FUSESandboxedProcessFactory factory(&platform_, {exe_}, run_as_, true);
219 MockSandboxedProcess sandbox_;
220 EXPECT_TRUE(ApplyConfiguration(factory, &sandbox_));
221}
222
223TEST_F(FUSESandboxedProcessFactoryTest, NetworkEnabled_Crostini) {
Derek Basehorea057a972021-01-19 18:45:58 -0800224 EXPECT_CALL(platform_, PathExists(kCgroup)).WillOnce(Return(true));
Sergei Datsenkoadd282e2020-11-16 15:41:24 +1100225 EXPECT_CALL(platform_, PathExists(exe_.value())).WillOnce(Return(true));
226 EXPECT_CALL(platform_, PathExists("/etc/hosts.d")).WillOnce(Return(true));
227 FUSESandboxedProcessFactory factory(&platform_, {exe_}, run_as_, true);
228 MockSandboxedProcess sandbox_;
229 EXPECT_TRUE(ApplyConfiguration(factory, &sandbox_));
230}
231
232TEST_F(FUSESandboxedProcessFactoryTest, SupplementaryGroups) {
233 FUSESandboxedProcessFactory factory(&platform_, {exe_}, run_as_, false,
234 {11, 22, 33});
235 MockSandboxedProcess sandbox_;
236 EXPECT_TRUE(ApplyConfiguration(factory, &sandbox_));
237}
238
239TEST_F(FUSESandboxedProcessFactoryTest, MountNamespace) {
240 base::FilePath mount_ns(base::StringPrintf("/proc/%d/ns/mnt", getpid()));
241 FUSESandboxedProcessFactory factory(&platform_, {exe_}, run_as_, false, {},
242 mount_ns);
243 MockSandboxedProcess sandbox_;
244 EXPECT_TRUE(ApplyConfiguration(factory, &sandbox_));
245}
246
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100247class FUSEMounterTest : public ::testing::Test {
248 public:
249 FUSEMounterTest() : mounter_(&platform_, &process_reaper_) {}
250
251 protected:
252 MockFUSEPlatform platform_;
253 brillo::ProcessReaper process_reaper_;
254 FUSEMounterForTesting mounter_;
255};
256
257TEST_F(FUSEMounterTest, MountingUnprivileged) {
258 EXPECT_CALL(platform_,
Sergei Datsenkoe2d1d0b2020-11-18 12:48:13 +1100259 Mount("fuse:source", kMountDir, "fuse.fusefs",
Sergei Datsenko3928f782020-12-31 09:14:04 +1100260 kFUSEMountFlags | MS_NOSYMFOLLOW,
Sergei Datsenkoadd282e2020-11-16 15:41:24 +1100261 EndsWith(",user_id=1000,group_id=1001,allow_other,default_"
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100262 "permissions,rootmode=40000")))
263 .WillOnce(Return(MOUNT_ERROR_NONE));
Sergei Datsenko88035aa2020-11-15 00:24:01 +1100264 auto process_ptr = std::make_unique<MockSandboxedProcess>();
265 EXPECT_CALL(*process_ptr, StartImpl).WillOnce(Return(123));
266 EXPECT_CALL(mounter_, PrepareSandbox("source", base::FilePath(kMountDir),
267 ElementsAre("arg1", "arg2", "arg3"), _))
268 .WillOnce(Return(ByMove(std::move(process_ptr))));
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100269 // The MountPoint returned by Mount() will unmount when it is destructed.
270 EXPECT_CALL(platform_, Unmount(kMountDir, 0))
271 .WillOnce(Return(MOUNT_ERROR_NONE));
272
273 MountErrorType error = MOUNT_ERROR_UNKNOWN;
274 auto mount_point = mounter_.Mount("source", base::FilePath(kMountDir),
275 {"arg1", "arg2", "arg3"}, &error);
276 EXPECT_TRUE(mount_point);
277 EXPECT_EQ(MOUNT_ERROR_NONE, error);
278}
279
280TEST_F(FUSEMounterTest, MountingUnprivileged_ReadOnly) {
281 EXPECT_CALL(platform_, Mount(_, kMountDir, _,
Sergei Datsenko3928f782020-12-31 09:14:04 +1100282 kFUSEMountFlags | MS_NOSYMFOLLOW | MS_RDONLY, _))
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100283 .WillOnce(Return(MOUNT_ERROR_NONE));
Sergei Datsenko88035aa2020-11-15 00:24:01 +1100284 auto process_ptr = std::make_unique<MockSandboxedProcess>();
285 EXPECT_CALL(*process_ptr, StartImpl).WillOnce(Return(123));
286 EXPECT_CALL(mounter_, PrepareSandbox(kSomeSource, base::FilePath(kMountDir),
287 ElementsAre("arg1", "arg2", "ro"), _))
288 .WillOnce(Return(ByMove(std::move(process_ptr))));
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100289 // The MountPoint returned by Mount() will unmount when it is destructed.
290 EXPECT_CALL(platform_, Unmount(kMountDir, 0))
291 .WillOnce(Return(MOUNT_ERROR_NONE));
292
293 MountErrorType error = MOUNT_ERROR_UNKNOWN;
294 auto mount_point = mounter_.Mount(kSomeSource, base::FilePath(kMountDir),
295 {"arg1", "arg2", "ro"}, &error);
296 EXPECT_TRUE(mount_point);
297 EXPECT_EQ(MOUNT_ERROR_NONE, error);
298}
299
Sergei Datsenkoe2d1d0b2020-11-18 12:48:13 +1100300TEST_F(FUSEMounterTest, MountingUnprivileged_BlockDevice) {
301 EXPECT_CALL(platform_,
302 Mount("/dev/foobar", kMountDir, "fuseblk.fusefs",
Sergei Datsenko3928f782020-12-31 09:14:04 +1100303 kFUSEMountFlags | MS_NOSYMFOLLOW,
Sergei Datsenkoe2d1d0b2020-11-18 12:48:13 +1100304 EndsWith(",user_id=1000,group_id=1001,allow_other,default_"
305 "permissions,rootmode=40000")))
306 .WillOnce(Return(MOUNT_ERROR_NONE));
307 auto process_ptr = std::make_unique<MockSandboxedProcess>();
308 EXPECT_CALL(*process_ptr, StartImpl).WillOnce(Return(123));
309 EXPECT_CALL(mounter_,
310 PrepareSandbox("/dev/foobar", base::FilePath(kMountDir), _, _))
311 .WillOnce(Return(ByMove(std::move(process_ptr))));
312 // The MountPoint returned by Mount() will unmount when it is destructed.
313 EXPECT_CALL(platform_, Unmount(kMountDir, 0))
314 .WillOnce(Return(MOUNT_ERROR_NONE));
315
316 MountErrorType error = MOUNT_ERROR_UNKNOWN;
317 auto mount_point =
318 mounter_.Mount("/dev/foobar", base::FilePath(kMountDir), {}, &error);
319 EXPECT_TRUE(mount_point);
320 EXPECT_EQ(MOUNT_ERROR_NONE, error);
321}
322
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100323TEST_F(FUSEMounterTest, MountingUnprivileged_MountFailed) {
324 EXPECT_CALL(platform_, Mount(_, kMountDir, _, _, _))
325 .WillOnce(Return(MOUNT_ERROR_UNKNOWN_FILESYSTEM));
Sergei Datsenko88035aa2020-11-15 00:24:01 +1100326 EXPECT_CALL(mounter_, PrepareSandbox).Times(0);
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100327 EXPECT_CALL(platform_, Unmount(kMountDir, _)).Times(0);
328
329 MountErrorType error = MOUNT_ERROR_UNKNOWN;
330 auto mount_point =
331 mounter_.Mount(kSomeSource, base::FilePath(kMountDir), {}, &error);
332 EXPECT_FALSE(mount_point);
333 EXPECT_EQ(MOUNT_ERROR_UNKNOWN_FILESYSTEM, error);
334}
335
Sergei Datsenko88035aa2020-11-15 00:24:01 +1100336TEST_F(FUSEMounterTest, MountingUnprivileged_SandboxFailed) {
337 EXPECT_CALL(platform_, Mount(_, kMountDir, _, _, _))
338 .WillOnce(Return(MOUNT_ERROR_NONE));
339 EXPECT_CALL(mounter_, PrepareSandbox)
340 .WillOnce(DoAll(SetArgPointee<3>(MOUNT_ERROR_INVALID_MOUNT_OPTIONS),
341 Return(ByMove(nullptr))));
342 EXPECT_CALL(platform_, Unmount(kMountDir, MNT_FORCE | MNT_DETACH))
343 .WillOnce(Return(MOUNT_ERROR_NONE));
344
345 MountErrorType error = MOUNT_ERROR_UNKNOWN;
346 auto mount_point =
347 mounter_.Mount(kSomeSource, base::FilePath(kMountDir), {}, &error);
348 EXPECT_FALSE(mount_point);
349 EXPECT_EQ(MOUNT_ERROR_INVALID_MOUNT_OPTIONS, error);
350}
351
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100352TEST_F(FUSEMounterTest, MountingUnprivileged_AppFailed) {
353 EXPECT_CALL(platform_, Mount(_, kMountDir, _, _, _))
354 .WillOnce(Return(MOUNT_ERROR_NONE));
Sergei Datsenko88035aa2020-11-15 00:24:01 +1100355 auto process_ptr = std::make_unique<MockSandboxedProcess>();
356 EXPECT_CALL(*process_ptr, StartImpl).WillOnce(Return(123));
357 EXPECT_CALL(*process_ptr, WaitNonBlockingImpl).WillOnce(Return(1));
358 EXPECT_CALL(mounter_, PrepareSandbox(_, base::FilePath(kMountDir), _, _))
359 .WillOnce(Return(ByMove(std::move(process_ptr))));
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100360 EXPECT_CALL(platform_, Unmount(kMountDir, MNT_FORCE | MNT_DETACH))
361 .WillOnce(Return(MOUNT_ERROR_NONE));
362
363 MountErrorType error = MOUNT_ERROR_UNKNOWN;
364 auto mount_point =
365 mounter_.Mount(kSomeSource, base::FilePath(kMountDir), {}, &error);
366 EXPECT_FALSE(mount_point);
367 EXPECT_EQ(MOUNT_ERROR_MOUNT_PROGRAM_FAILED, error);
368}
369
370TEST_F(FUSEMounterTest, MountPoint_UnmountTwice) {
371 EXPECT_CALL(platform_, Mount(_, kMountDir, _, _, _))
372 .WillOnce(Return(MOUNT_ERROR_NONE));
Sergei Datsenko88035aa2020-11-15 00:24:01 +1100373 auto process_ptr = std::make_unique<MockSandboxedProcess>();
374 EXPECT_CALL(*process_ptr, StartImpl).WillOnce(Return(123));
375 EXPECT_CALL(mounter_, PrepareSandbox(_, base::FilePath(kMountDir), _, _))
376 .WillOnce(Return(ByMove(std::move(process_ptr))));
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100377 // Even though Unmount() is called twice, the underlying unmount should only
378 // be done once.
379 EXPECT_CALL(platform_, Unmount(kMountDir, 0))
380 .WillOnce(Return(MOUNT_ERROR_NONE));
381
382 MountErrorType error = MOUNT_ERROR_UNKNOWN;
383 auto mount_point =
384 mounter_.Mount(kSomeSource, base::FilePath(kMountDir), {}, &error);
385 EXPECT_TRUE(mount_point);
386 EXPECT_EQ(MOUNT_ERROR_NONE, error);
387
388 EXPECT_EQ(MOUNT_ERROR_NONE, mount_point->Unmount());
389 EXPECT_EQ(MOUNT_ERROR_PATH_NOT_MOUNTED, mount_point->Unmount());
390}
391
392TEST_F(FUSEMounterTest, MountPoint_UnmountFailure) {
393 EXPECT_CALL(platform_, Mount(_, kMountDir, _, _, _))
394 .WillOnce(Return(MOUNT_ERROR_NONE));
Sergei Datsenko88035aa2020-11-15 00:24:01 +1100395 auto process_ptr = std::make_unique<MockSandboxedProcess>();
396 EXPECT_CALL(*process_ptr, StartImpl).WillOnce(Return(123));
397 EXPECT_CALL(mounter_, PrepareSandbox(_, base::FilePath(kMountDir), _, _))
398 .WillOnce(Return(ByMove(std::move(process_ptr))));
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100399 // If an Unmount fails, we should be able to retry.
400 EXPECT_CALL(platform_, Unmount(kMountDir, 0))
401 .WillOnce(Return(MOUNT_ERROR_UNKNOWN))
402 .WillOnce(Return(MOUNT_ERROR_NONE));
403
404 MountErrorType error = MOUNT_ERROR_UNKNOWN;
405 auto mount_point =
406 mounter_.Mount(kSomeSource, base::FilePath(kMountDir), {}, &error);
407 EXPECT_TRUE(mount_point);
408 EXPECT_EQ(MOUNT_ERROR_NONE, error);
409
410 EXPECT_EQ(MOUNT_ERROR_UNKNOWN, mount_point->Unmount());
411 EXPECT_EQ(MOUNT_ERROR_NONE, mount_point->Unmount());
412}
413
414TEST_F(FUSEMounterTest, MountPoint_UnmountBusy) {
415 EXPECT_CALL(platform_, Mount(_, kMountDir, _, _, _))
416 .WillOnce(Return(MOUNT_ERROR_NONE));
Sergei Datsenko88035aa2020-11-15 00:24:01 +1100417 auto process_ptr = std::make_unique<MockSandboxedProcess>();
418 EXPECT_CALL(*process_ptr, StartImpl).WillOnce(Return(123));
419 EXPECT_CALL(mounter_, PrepareSandbox(_, base::FilePath(kMountDir), _, _))
420 .WillOnce(Return(ByMove(std::move(process_ptr))));
Sergei Datsenko199f4f42020-10-08 10:47:12 +1100421 EXPECT_CALL(platform_, Unmount(kMountDir, 0))
422 .WillOnce(Return(MOUNT_ERROR_PATH_ALREADY_MOUNTED));
423 EXPECT_CALL(platform_, Unmount(kMountDir, MNT_FORCE | MNT_DETACH))
424 .WillOnce(Return(MOUNT_ERROR_NONE));
425
426 MountErrorType error = MOUNT_ERROR_UNKNOWN;
427 auto mount_point =
428 mounter_.Mount(kSomeSource, base::FilePath(kMountDir), {}, &error);
429 EXPECT_TRUE(mount_point);
430 EXPECT_EQ(MOUNT_ERROR_NONE, error);
431
432 EXPECT_EQ(MOUNT_ERROR_NONE, mount_point->Unmount());
433}
434
Sergei Datsenko6907a132019-04-01 11:26:56 +1100435} // namespace cros_disks