1 // Copyright 2018 The Chromium 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 "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
6
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/run_loop.h"
11 #include "base/test/scoped_feature_list.h"
12 #include "chrome/browser/chromeos/arc/session/arc_session_manager.h"
13 #include "chrome/browser/chromeos/arc/test/test_arc_session_manager.h"
14 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
15 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
16 #include "chrome/browser/chromeos/crostini/crostini_util.h"
17 #include "chrome/browser/chromeos/file_manager/fake_disk_mount_manager.h"
18 #include "chrome/browser/chromeos/file_manager/path_util.h"
19 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
20 #include "chrome/browser/chromeos/file_manager/volume_manager_factory.h"
21 #include "chrome/browser/chromeos/file_system_provider/service_factory.h"
22 #include "chrome/browser/chromeos/guest_os/guest_os_pref_names.h"
23 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
24 #include "chrome/browser/component_updater/fake_cros_component_manager.h"
25 #include "chrome/common/chrome_features.h"
26 #include "chrome/test/base/browser_process_platform_part_test_api_chromeos.h"
27 #include "chrome/test/base/scoped_testing_local_state.h"
28 #include "chrome/test/base/testing_browser_process.h"
29 #include "chrome/test/base/testing_profile.h"
30 #include "chromeos/constants/chromeos_features.h"
31 #include "chromeos/dbus/dbus_thread_manager.h"
32 #include "chromeos/dbus/dlcservice/dlcservice_client.h"
33 #include "chromeos/dbus/fake_cicerone_client.h"
34 #include "chromeos/dbus/fake_concierge_client.h"
35 #include "chromeos/dbus/fake_seneschal_client.h"
36 #include "chromeos/dbus/seneschal/seneschal_service.pb.h"
37 #include "chromeos/disks/disk_mount_manager.h"
38 #include "components/account_id/account_id.h"
39 #include "components/arc/arc_util.h"
40 #include "components/arc/session/arc_session_runner.h"
41 #include "components/arc/test/fake_arc_session.h"
42 #include "components/drive/drive_pref_names.h"
43 #include "components/prefs/pref_service.h"
44 #include "components/prefs/scoped_user_pref_update.h"
45 #include "components/user_manager/scoped_user_manager.h"
46 #include "content/public/test/browser_task_environment.h"
47 #include "storage/browser/file_system/external_mount_points.h"
48 #include "testing/gtest/include/gtest/gtest.h"
49
50 namespace {
51
52 // Creates a new VolumeManager for tests.
53 // By default, VolumeManager KeyedService is null for testing.
BuildVolumeManager(content::BrowserContext * context)54 std::unique_ptr<KeyedService> BuildVolumeManager(
55 content::BrowserContext* context) {
56 return std::make_unique<file_manager::VolumeManager>(
57 Profile::FromBrowserContext(context),
58 nullptr /* drive_integration_service */,
59 nullptr /* power_manager_client */,
60 chromeos::disks::DiskMountManager::GetInstance(),
61 nullptr /* file_system_provider_service */,
62 file_manager::VolumeManager::GetMtpStorageInfoCallback());
63 }
64
65 } // namespace
66
67 namespace guest_os {
68
69 class GuestOsSharePathTest : public testing::Test {
70 public:
71 const bool PERSIST_YES = true;
72 const bool PERSIST_NO = false;
73 enum class Persist { NO, YES };
74 enum class SeneschalClientCalled { NO, YES };
75 enum class Success { NO, YES };
76
SharePathCallback(const std::string & expected_vm_name,Persist expected_persist,SeneschalClientCalled expected_seneschal_client_called,const vm_tools::seneschal::SharePathRequest::StorageLocation * expected_seneschal_storage_location,const std::string & expected_seneschal_path,Success expected_success,const std::string & expected_failure_reason,const base::FilePath & container_path,bool success,const std::string & failure_reason)77 void SharePathCallback(
78 const std::string& expected_vm_name,
79 Persist expected_persist,
80 SeneschalClientCalled expected_seneschal_client_called,
81 const vm_tools::seneschal::SharePathRequest::StorageLocation*
82 expected_seneschal_storage_location,
83 const std::string& expected_seneschal_path,
84 Success expected_success,
85 const std::string& expected_failure_reason,
86 const base::FilePath& container_path,
87 bool success,
88 const std::string& failure_reason) {
89 const base::DictionaryValue* prefs =
90 profile()->GetPrefs()->GetDictionary(prefs::kGuestOSPathsSharedToVms);
91 EXPECT_TRUE(prefs->HasKey(shared_path_.value()));
92 EXPECT_EQ(prefs->FindKey(shared_path_.value())->GetList().size(), 1U);
93 EXPECT_EQ(prefs->FindKey(shared_path_.value())->GetList()[0].GetString(),
94 crostini::kCrostiniDefaultVmName);
95 if (expected_persist == Persist::YES) {
96 EXPECT_EQ(prefs->size(), 2U);
97 EXPECT_TRUE(prefs->HasKey(share_path_.value()));
98 EXPECT_EQ(prefs->FindKey(share_path_.value())->GetList().size(), 1U);
99 EXPECT_EQ(prefs->FindKey(share_path_.value())->GetList()[0].GetString(),
100 expected_vm_name);
101 } else {
102 EXPECT_EQ(prefs->size(), 1U);
103 }
104 EXPECT_EQ(fake_seneschal_client_->share_path_called(),
105 expected_seneschal_client_called == SeneschalClientCalled::YES);
106 if (expected_seneschal_client_called == SeneschalClientCalled::YES) {
107 EXPECT_EQ(
108 fake_seneschal_client_->last_share_path_request().storage_location(),
109 *expected_seneschal_storage_location);
110 EXPECT_EQ(fake_seneschal_client_->last_share_path_request()
111 .shared_path()
112 .path(),
113 expected_seneschal_path);
114 }
115 EXPECT_EQ(success, expected_success == Success::YES);
116 EXPECT_EQ(failure_reason, expected_failure_reason);
117 run_loop()->Quit();
118 }
119
SeneschalSharePathCallback(const std::string & expected_operation,const base::FilePath & expected_path,const std::string & expected_vm_name,Persist expected_persist,SeneschalClientCalled expected_seneschal_client_called,const vm_tools::seneschal::SharePathRequest::StorageLocation * expected_seneschal_storage_location,const std::string & expected_seneschal_path,Success expected_success,const std::string & expected_failure_reason,const std::string & operation,const base::FilePath & cros_path,const base::FilePath & container_path,bool success,const std::string & failure_reason)120 void SeneschalSharePathCallback(
121 const std::string& expected_operation,
122 const base::FilePath& expected_path,
123 const std::string& expected_vm_name,
124 Persist expected_persist,
125 SeneschalClientCalled expected_seneschal_client_called,
126 const vm_tools::seneschal::SharePathRequest::StorageLocation*
127 expected_seneschal_storage_location,
128 const std::string& expected_seneschal_path,
129 Success expected_success,
130 const std::string& expected_failure_reason,
131 const std::string& operation,
132 const base::FilePath& cros_path,
133 const base::FilePath& container_path,
134 bool success,
135 const std::string& failure_reason) {
136 EXPECT_EQ(expected_operation, operation);
137 EXPECT_EQ(expected_path, cros_path);
138 SharePathCallback(
139 expected_vm_name, expected_persist, expected_seneschal_client_called,
140 expected_seneschal_storage_location, expected_seneschal_path,
141 expected_success, expected_failure_reason, container_path, success,
142 failure_reason);
143 }
144
SharePersistedPathsCallback(bool success,const std::string & failure_reason)145 void SharePersistedPathsCallback(bool success,
146 const std::string& failure_reason) {
147 EXPECT_TRUE(success);
148 EXPECT_EQ(profile()
149 ->GetPrefs()
150 ->GetDictionary(prefs::kGuestOSPathsSharedToVms)
151 ->size(),
152 2U);
153 run_loop()->Quit();
154 }
155
SharePathErrorVmNotRunningCallback(base::OnceClosure closure,bool success,std::string failure_reason)156 void SharePathErrorVmNotRunningCallback(base::OnceClosure closure,
157 bool success,
158 std::string failure_reason) {
159 EXPECT_FALSE(fake_seneschal_client_->share_path_called());
160 EXPECT_EQ(success, false);
161 EXPECT_EQ(failure_reason, "Cannot share, VM not running");
162 std::move(closure).Run();
163 }
164
UnsharePathCallback(const base::FilePath & path,Persist expected_persist,SeneschalClientCalled expected_seneschal_client_called,const std::string & expected_seneschal_path,Success expected_success,const std::string & expected_failure_reason,bool success,const std::string & failure_reason)165 void UnsharePathCallback(
166 const base::FilePath& path,
167 Persist expected_persist,
168 SeneschalClientCalled expected_seneschal_client_called,
169 const std::string& expected_seneschal_path,
170 Success expected_success,
171 const std::string& expected_failure_reason,
172 bool success,
173 const std::string& failure_reason) {
174 const base::DictionaryValue* prefs =
175 profile()->GetPrefs()->GetDictionary(prefs::kGuestOSPathsSharedToVms);
176 if (expected_persist == Persist::YES) {
177 EXPECT_TRUE(prefs->HasKey(path.value()));
178 } else {
179 EXPECT_FALSE(prefs->HasKey(path.value()));
180 }
181 EXPECT_EQ(fake_seneschal_client_->unshare_path_called(),
182 expected_seneschal_client_called == SeneschalClientCalled::YES);
183 if (expected_seneschal_client_called == SeneschalClientCalled::YES) {
184 EXPECT_EQ(fake_seneschal_client_->last_unshare_path_request().path(),
185 expected_seneschal_path);
186 }
187 EXPECT_EQ(success, expected_success == Success::YES);
188 EXPECT_EQ(failure_reason, expected_failure_reason);
189 run_loop()->Quit();
190 }
191
SeneschalUnsharePathCallback(const std::string expected_operation,const base::FilePath & expected_path,Persist expected_persist,SeneschalClientCalled expected_seneschal_client_called,const std::string & expected_seneschal_path,Success expected_success,const std::string & expected_failure_reason,const std::string & operation,const base::FilePath & cros_path,const base::FilePath & container_path,bool success,const std::string & failure_reason)192 void SeneschalUnsharePathCallback(
193 const std::string expected_operation,
194 const base::FilePath& expected_path,
195 Persist expected_persist,
196 SeneschalClientCalled expected_seneschal_client_called,
197 const std::string& expected_seneschal_path,
198 Success expected_success,
199 const std::string& expected_failure_reason,
200 const std::string& operation,
201 const base::FilePath& cros_path,
202 const base::FilePath& container_path,
203 bool success,
204 const std::string& failure_reason) {
205 EXPECT_EQ(expected_operation, operation);
206 EXPECT_EQ(expected_path, cros_path);
207 UnsharePathCallback(cros_path, expected_persist,
208 expected_seneschal_client_called,
209 expected_seneschal_path, expected_success,
210 expected_failure_reason, success, failure_reason);
211 }
212
GuestOsSharePathTest()213 GuestOsSharePathTest()
214 : local_state_(std::make_unique<ScopedTestingLocalState>(
215 TestingBrowserProcess::GetGlobal())),
216 browser_part_(g_browser_process->platform_part()) {
217 chromeos::DBusThreadManager::Initialize();
218 fake_concierge_client_ = static_cast<chromeos::FakeConciergeClient*>(
219 chromeos::DBusThreadManager::Get()->GetConciergeClient());
220 fake_seneschal_client_ = static_cast<chromeos::FakeSeneschalClient*>(
221 chromeos::DBusThreadManager::Get()->GetSeneschalClient());
222 }
223
~GuestOsSharePathTest()224 ~GuestOsSharePathTest() override { chromeos::DBusThreadManager::Shutdown(); }
225
SetUpVolume()226 void SetUpVolume() {
227 // Setup Downloads and path to share, which depend on MyFilesVolume flag,
228 // thus can't be on SetUp.
229 chromeos::disks::DiskMountManager::InitializeForTesting(
230 new file_manager::FakeDiskMountManager);
231 file_manager::VolumeManagerFactory::GetInstance()->SetTestingFactory(
232 profile(), base::BindRepeating(&BuildVolumeManager));
233 root_ = file_manager::util::GetMyFilesFolderForProfile(profile());
234 file_manager::VolumeManager::Get(profile())
235 ->RegisterDownloadsDirectoryForTesting(root_);
236 share_path_ = root_.Append("path-to-share");
237 shared_path_ = root_.Append("already-shared");
238 ASSERT_TRUE(base::CreateDirectory(shared_path_));
239 DictionaryPrefUpdate update(profile()->GetPrefs(),
240 prefs::kGuestOSPathsSharedToVms);
241 base::DictionaryValue* shared_paths = update.Get();
242 base::Value termina(base::Value::Type::LIST);
243 termina.Append(base::Value(crostini::kCrostiniDefaultVmName));
244 shared_paths->SetKey(shared_path_.value(), std::move(termina));
245 volume_downloads_ = file_manager::Volume::CreateForDownloads(root_);
246 guest_os_share_path_->RegisterSharedPath(crostini::kCrostiniDefaultVmName,
247 shared_path_);
248 // Run threads now to allow watcher for shared_path_ to start.
249 task_environment_.RunUntilIdle();
250 }
251
SetUp()252 void SetUp() override {
253 component_manager_ =
254 base::MakeRefCounted<component_updater::FakeCrOSComponentManager>();
255 component_manager_->set_supported_components({"cros-termina"});
256 component_manager_->ResetComponentState(
257 "cros-termina",
258 component_updater::FakeCrOSComponentManager::ComponentInfo(
259 component_updater::CrOSComponentManager::Error::NONE,
260 base::FilePath("/install/path"), base::FilePath("/mount/path")));
261 browser_part_.InitializeCrosComponentManager(component_manager_);
262 chromeos::DlcserviceClient::InitializeFake();
263
264 run_loop_ = std::make_unique<base::RunLoop>();
265 profile_ = std::make_unique<TestingProfile>();
266 guest_os_share_path_ = GuestOsSharePath::GetForProfile(profile());
267
268 // Setup for DriveFS.
269 scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
270 std::make_unique<chromeos::FakeChromeUserManager>());
271 account_id_ = AccountId::FromUserEmailGaiaId(
272 profile()->GetProfileUserName(), "12345");
273 GetFakeUserManager()->AddUser(account_id_);
274 profile()->GetPrefs()->SetString(drive::prefs::kDriveFsProfileSalt, "a");
275 drivefs_ =
276 base::FilePath("/media/fuse/drivefs-84675c855b63e12f384d45f033826980");
277
278 // Create 'vm-running' VM instance which is running.
279 crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
280 "vm-running");
281
282 g_browser_process->platform_part()
283 ->InitializeSchedulerConfigurationManager();
284
285 // Create ArcSessionManager for ARCVM testing.
286 arc_session_manager_ = arc::CreateTestArcSessionManager(
287 std::make_unique<arc::ArcSessionRunner>(
288 base::BindRepeating(arc::FakeArcSession::Create)));
289 }
290
TearDown()291 void TearDown() override {
292 arc_session_manager_.reset();
293 g_browser_process->platform_part()->ShutdownSchedulerConfigurationManager();
294 // Shutdown GuestOsSharePath to schedule FilePathWatchers to be destroyed,
295 // then run thread bundle to ensure they are.
296 guest_os_share_path_->Shutdown();
297 task_environment_.RunUntilIdle();
298 run_loop_.reset();
299 scoped_user_manager_.reset();
300 profile_.reset();
301 chromeos::DlcserviceClient::Shutdown();
302 browser_part_.ShutdownCrosComponentManager();
303 component_manager_.reset();
304 }
305
GetFakeUserManager() const306 chromeos::FakeChromeUserManager* GetFakeUserManager() const {
307 return static_cast<chromeos::FakeChromeUserManager*>(
308 user_manager::UserManager::Get());
309 }
310
311 protected:
run_loop()312 base::RunLoop* run_loop() { return run_loop_.get(); }
profile()313 Profile* profile() { return profile_.get(); }
314 base::FilePath root_;
315 base::FilePath share_path_;
316 base::FilePath shared_path_;
317 base::FilePath drivefs_;
318 std::unique_ptr<file_manager::Volume> volume_downloads_;
319
320 // Owned by chromeos::DBusThreadManager
321 chromeos::FakeSeneschalClient* fake_seneschal_client_;
322 chromeos::FakeConciergeClient* fake_concierge_client_;
323
324 content::BrowserTaskEnvironment task_environment_;
325 std::unique_ptr<base::RunLoop> run_loop_;
326 std::unique_ptr<TestingProfile> profile_;
327 GuestOsSharePath* guest_os_share_path_;
328 base::test::ScopedFeatureList features_;
329 std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
330 AccountId account_id_;
331 std::unique_ptr<arc::ArcSessionManager> arc_session_manager_;
332
333 private:
334 std::unique_ptr<ScopedTestingLocalState> local_state_;
335 scoped_refptr<component_updater::FakeCrOSComponentManager> component_manager_;
336 BrowserProcessPlatformPartTestApi browser_part_;
337
338 DISALLOW_COPY_AND_ASSIGN(GuestOsSharePathTest);
339 };
340
TEST_F(GuestOsSharePathTest,SuccessMyFilesRoot)341 TEST_F(GuestOsSharePathTest, SuccessMyFilesRoot) {
342 SetUpVolume();
343 base::FilePath my_files =
344 file_manager::util::GetMyFilesFolderForProfile(profile());
345 guest_os_share_path_->SharePath(
346 "vm-running", my_files, PERSIST_NO,
347 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
348 base::Unretained(this), "vm-running", Persist::NO,
349 SeneschalClientCalled::YES,
350 &vm_tools::seneschal::SharePathRequest::MY_FILES, "",
351 Success::YES, ""));
352 run_loop()->Run();
353 }
354
TEST_F(GuestOsSharePathTest,SuccessNoPersist)355 TEST_F(GuestOsSharePathTest, SuccessNoPersist) {
356 SetUpVolume();
357 guest_os_share_path_->SharePath(
358 "vm-running", share_path_, PERSIST_NO,
359 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
360 base::Unretained(this), "vm-running", Persist::NO,
361 SeneschalClientCalled::YES,
362 &vm_tools::seneschal::SharePathRequest::MY_FILES,
363 "path-to-share", Success::YES, ""));
364 run_loop()->Run();
365 }
366
TEST_F(GuestOsSharePathTest,SuccessPersist)367 TEST_F(GuestOsSharePathTest, SuccessPersist) {
368 SetUpVolume();
369 guest_os_share_path_->SharePath(
370 "vm-running", share_path_, PERSIST_YES,
371 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
372 base::Unretained(this), "vm-running", Persist::YES,
373 SeneschalClientCalled::YES,
374 &vm_tools::seneschal::SharePathRequest::MY_FILES,
375 "path-to-share", Success::YES, ""));
376 run_loop()->Run();
377 }
378
TEST_F(GuestOsSharePathTest,SuccessPluginVm)379 TEST_F(GuestOsSharePathTest, SuccessPluginVm) {
380 SetUpVolume();
381 guest_os_share_path_->SharePath(
382 "PvmDefault", share_path_, PERSIST_NO,
383 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
384 base::Unretained(this), "PvmDefault", Persist::NO,
385 SeneschalClientCalled::YES,
386 &vm_tools::seneschal::SharePathRequest::MY_FILES,
387 "path-to-share", Success::YES, ""));
388 run_loop()->Run();
389 }
390
391 // Tests that ARCVM can share path.
TEST_F(GuestOsSharePathTest,SuccessArcvm)392 TEST_F(GuestOsSharePathTest, SuccessArcvm) {
393 SetUpVolume();
394
395 // Set up VmInfo in |arc_session_manager_| to simulate a running ARCVM.
396 vm_tools::concierge::VmStartedSignal start_signal;
397 start_signal.set_name(arc::kArcVmName);
398 start_signal.mutable_vm_info()->set_seneschal_server_handle(1000UL);
399 arc_session_manager_->OnVmStarted(start_signal);
400
401 guest_os_share_path_->SharePath(
402 arc::kArcVmName, drivefs_.Append("root").Append("ArcvmTest"), PERSIST_NO,
403 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
404 base::Unretained(this), arc::kArcVmName, Persist::NO,
405 SeneschalClientCalled::YES,
406 &vm_tools::seneschal::SharePathRequest::DRIVEFS_MY_DRIVE,
407 "ArcvmTest", Success::YES, ""));
408 // Also validate the seneschal server handle.
409 EXPECT_EQ(1000UL, fake_seneschal_client_->last_share_path_request().handle());
410 run_loop()->Run();
411 }
412
TEST_F(GuestOsSharePathTest,SuccessDriveFsMyDrive)413 TEST_F(GuestOsSharePathTest, SuccessDriveFsMyDrive) {
414 SetUpVolume();
415 guest_os_share_path_->SharePath(
416 "vm-running", drivefs_.Append("root").Append("my"), PERSIST_NO,
417 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
418 base::Unretained(this), "vm-running", Persist::NO,
419 SeneschalClientCalled::YES,
420 &vm_tools::seneschal::SharePathRequest::DRIVEFS_MY_DRIVE,
421 "my", Success::YES, ""));
422 run_loop()->Run();
423 }
424
TEST_F(GuestOsSharePathTest,SuccessDriveFsMyDriveRoot)425 TEST_F(GuestOsSharePathTest, SuccessDriveFsMyDriveRoot) {
426 SetUpVolume();
427 guest_os_share_path_->SharePath(
428 "vm-running", drivefs_.Append("root"), PERSIST_NO,
429 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
430 base::Unretained(this), "vm-running", Persist::NO,
431 SeneschalClientCalled::YES,
432 &vm_tools::seneschal::SharePathRequest::DRIVEFS_MY_DRIVE,
433 "", Success::YES, ""));
434 run_loop()->Run();
435 }
436
TEST_F(GuestOsSharePathTest,FailDriveFsRoot)437 TEST_F(GuestOsSharePathTest, FailDriveFsRoot) {
438 SetUpVolume();
439 guest_os_share_path_->SharePath(
440 "vm-running", drivefs_, PERSIST_NO,
441 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
442 base::Unretained(this), "vm-running", Persist::NO,
443 SeneschalClientCalled::NO, nullptr, "", Success::NO,
444 "Path is not allowed"));
445 run_loop()->Run();
446 }
447
TEST_F(GuestOsSharePathTest,SuccessDriveFsTeamDrives)448 TEST_F(GuestOsSharePathTest, SuccessDriveFsTeamDrives) {
449 SetUpVolume();
450 guest_os_share_path_->SharePath(
451 "vm-running", drivefs_.Append("team_drives").Append("team"), PERSIST_NO,
452 base::BindOnce(
453 &GuestOsSharePathTest::SharePathCallback, base::Unretained(this),
454 "vm-running", Persist::NO, SeneschalClientCalled::YES,
455 &vm_tools::seneschal::SharePathRequest::DRIVEFS_TEAM_DRIVES, "team",
456 Success::YES, ""));
457 run_loop()->Run();
458 }
459
460 // TODO(crbug.com/917920): Enable when DriveFS enforces allowed write paths.
TEST_F(GuestOsSharePathTest,DISABLED_SuccessDriveFsComputersGrandRoot)461 TEST_F(GuestOsSharePathTest, DISABLED_SuccessDriveFsComputersGrandRoot) {
462 SetUpVolume();
463 guest_os_share_path_->SharePath(
464 "vm-running", drivefs_.Append("Computers"), PERSIST_NO,
465 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
466 base::Unretained(this), "vm-running", Persist::NO,
467 SeneschalClientCalled::YES,
468 &vm_tools::seneschal::SharePathRequest::DRIVEFS_COMPUTERS,
469 "pc", Success::YES, ""));
470 run_loop()->Run();
471 }
472
473 // TODO(crbug.com/917920): Remove when DriveFS enforces allowed write paths.
TEST_F(GuestOsSharePathTest,Bug917920DriveFsComputersGrandRoot)474 TEST_F(GuestOsSharePathTest, Bug917920DriveFsComputersGrandRoot) {
475 SetUpVolume();
476 guest_os_share_path_->SharePath(
477 "vm-running", drivefs_.Append("Computers"), PERSIST_NO,
478 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
479 base::Unretained(this), "vm-running", Persist::NO,
480 SeneschalClientCalled::NO, nullptr, "", Success::NO,
481 "Path is not allowed"));
482 run_loop()->Run();
483 }
484
485 // TODO(crbug.com/917920): Enable when DriveFS enforces allowed write paths.
TEST_F(GuestOsSharePathTest,DISABLED_SuccessDriveFsComputerRoot)486 TEST_F(GuestOsSharePathTest, DISABLED_SuccessDriveFsComputerRoot) {
487 SetUpVolume();
488 guest_os_share_path_->SharePath(
489 "vm-running", drivefs_.Append("Computers").Append("pc"), PERSIST_NO,
490 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
491 base::Unretained(this), "vm-running", Persist::NO,
492 SeneschalClientCalled::YES,
493 &vm_tools::seneschal::SharePathRequest::DRIVEFS_COMPUTERS,
494 "pc", Success::YES, ""));
495 run_loop()->Run();
496 }
497
498 // TODO(crbug.com/917920): Remove when DriveFS enforces allowed write paths.
TEST_F(GuestOsSharePathTest,Bug917920DriveFsComputerRoot)499 TEST_F(GuestOsSharePathTest, Bug917920DriveFsComputerRoot) {
500 SetUpVolume();
501 guest_os_share_path_->SharePath(
502 "vm-running", drivefs_.Append("Computers").Append("pc"), PERSIST_NO,
503 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
504 base::Unretained(this), "vm-running", Persist::NO,
505 SeneschalClientCalled::NO, nullptr, "", Success::NO,
506 "Path is not allowed"));
507 run_loop()->Run();
508 }
509
TEST_F(GuestOsSharePathTest,SuccessDriveFsComputersLevel3)510 TEST_F(GuestOsSharePathTest, SuccessDriveFsComputersLevel3) {
511 SetUpVolume();
512 guest_os_share_path_->SharePath(
513 "vm-running",
514 drivefs_.Append("Computers").Append("pc").Append("SyncFolder"),
515 PERSIST_NO,
516 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
517 base::Unretained(this), "vm-running", Persist::NO,
518 SeneschalClientCalled::YES,
519 &vm_tools::seneschal::SharePathRequest::DRIVEFS_COMPUTERS,
520 "pc/SyncFolder", Success::YES, ""));
521 run_loop()->Run();
522 }
523
TEST_F(GuestOsSharePathTest,FailDriveFsTrash)524 TEST_F(GuestOsSharePathTest, FailDriveFsTrash) {
525 SetUpVolume();
526 guest_os_share_path_->SharePath(
527 "vm-running", drivefs_.Append(".Trash").Append("in-the-trash"),
528 PERSIST_NO,
529 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
530 base::Unretained(this), "vm-running", Persist::NO,
531 SeneschalClientCalled::NO, nullptr, "", Success::NO,
532 "Path is not allowed"));
533 run_loop()->Run();
534 }
535
TEST_F(GuestOsSharePathTest,SuccessRemovable)536 TEST_F(GuestOsSharePathTest, SuccessRemovable) {
537 SetUpVolume();
538 guest_os_share_path_->SharePath(
539 "vm-running", base::FilePath("/media/removable/MyUSB"), PERSIST_NO,
540 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
541 base::Unretained(this), "vm-running", Persist::NO,
542 SeneschalClientCalled::YES,
543 &vm_tools::seneschal::SharePathRequest::REMOVABLE, "MyUSB",
544 Success::YES, ""));
545 run_loop()->Run();
546 }
547
TEST_F(GuestOsSharePathTest,FailRemovableRoot)548 TEST_F(GuestOsSharePathTest, FailRemovableRoot) {
549 SetUpVolume();
550 guest_os_share_path_->SharePath(
551 "vm-running", base::FilePath("/media/removable"), PERSIST_NO,
552 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
553 base::Unretained(this), "vm-running", Persist::NO,
554 SeneschalClientCalled::NO, nullptr, "", Success::NO,
555 "Path is not allowed"));
556 run_loop()->Run();
557 }
558
TEST_F(GuestOsSharePathTest,SuccessSystemFonts)559 TEST_F(GuestOsSharePathTest, SuccessSystemFonts) {
560 SetUpVolume();
561 guest_os_share_path_->SharePath(
562 "vm-running", base::FilePath("/usr/share/fonts"), PERSIST_NO,
563 base::BindOnce(
564 &GuestOsSharePathTest::SharePathCallback, base::Unretained(this),
565 "vm-running", Persist::NO, SeneschalClientCalled::YES,
566 &vm_tools::seneschal::SharePathRequest::FONTS, "", Success::YES, ""));
567 run_loop()->Run();
568 }
569
TEST_F(GuestOsSharePathTest,SharePathErrorSeneschal)570 TEST_F(GuestOsSharePathTest, SharePathErrorSeneschal) {
571 features_.InitWithFeatures({features::kCrostini}, {});
572 GetFakeUserManager()->LoginUser(account_id_);
573 SetUpVolume();
574 vm_tools::concierge::StartVmResponse start_vm_response;
575 start_vm_response.set_status(vm_tools::concierge::VM_STATUS_RUNNING);
576 start_vm_response.mutable_vm_info()->set_seneschal_server_handle(123);
577 fake_concierge_client_->set_start_vm_response(start_vm_response);
578
579 vm_tools::seneschal::SharePathResponse share_path_response;
580 share_path_response.set_success(false);
581 share_path_response.set_failure_reason("test failure");
582 fake_seneschal_client_->set_share_path_response(share_path_response);
583
584 guest_os_share_path_->SharePath(
585 "error-seneschal", share_path_, PERSIST_YES,
586 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
587 base::Unretained(this), "error-seneschal", Persist::YES,
588 SeneschalClientCalled::YES,
589 &vm_tools::seneschal::SharePathRequest::MY_FILES,
590 "path-to-share", Success::NO, "test failure"));
591 run_loop()->Run();
592 }
593
TEST_F(GuestOsSharePathTest,SharePathErrorPathNotAbsolute)594 TEST_F(GuestOsSharePathTest, SharePathErrorPathNotAbsolute) {
595 SetUpVolume();
596 const base::FilePath path("not/absolute/dir");
597 guest_os_share_path_->SharePath(
598 "vm-running", path, PERSIST_YES,
599 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
600 base::Unretained(this), "vm-running", Persist::NO,
601 SeneschalClientCalled::NO, nullptr, "", Success::NO,
602 "Path must be absolute"));
603 run_loop()->Run();
604 }
605
TEST_F(GuestOsSharePathTest,SharePathErrorReferencesParent)606 TEST_F(GuestOsSharePathTest, SharePathErrorReferencesParent) {
607 SetUpVolume();
608 const base::FilePath path("/path/../references/parent");
609 guest_os_share_path_->SharePath(
610 "vm-running", path, PERSIST_NO,
611 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
612 base::Unretained(this), "vm-running", Persist::NO,
613 SeneschalClientCalled::NO, nullptr, "", Success::NO,
614 "Path must be absolute"));
615 run_loop()->Run();
616 }
617
TEST_F(GuestOsSharePathTest,SharePathErrorNotUnderDownloads)618 TEST_F(GuestOsSharePathTest, SharePathErrorNotUnderDownloads) {
619 SetUpVolume();
620 const base::FilePath path("/not/under/downloads");
621 guest_os_share_path_->SharePath(
622 "vm-running", path, PERSIST_YES,
623 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
624 base::Unretained(this), "vm-running", Persist::NO,
625 SeneschalClientCalled::NO, nullptr, "", Success::NO,
626 "Path is not allowed"));
627 run_loop()->Run();
628 }
629
TEST_F(GuestOsSharePathTest,SharePathVmToBeRestarted)630 TEST_F(GuestOsSharePathTest, SharePathVmToBeRestarted) {
631 features_.InitWithFeatures({features::kCrostini}, {});
632 GetFakeUserManager()->LoginUser(account_id_);
633 SetUpVolume();
634 guest_os_share_path_->SharePath(
635 "vm-to-be-started", share_path_, PERSIST_YES,
636 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
637 base::Unretained(this), "vm-to-be-started", Persist::YES,
638 SeneschalClientCalled::YES,
639 &vm_tools::seneschal::SharePathRequest::MY_FILES,
640 "path-to-share", Success::YES, ""));
641 run_loop()->Run();
642 }
643
TEST_F(GuestOsSharePathTest,SharePathErrorVmCouldNotBeStarted)644 TEST_F(GuestOsSharePathTest, SharePathErrorVmCouldNotBeStarted) {
645 SetUpVolume();
646 vm_tools::concierge::StartVmResponse start_vm_response;
647 start_vm_response.set_status(vm_tools::concierge::VM_STATUS_FAILURE);
648 fake_concierge_client_->set_start_vm_response(start_vm_response);
649
650 guest_os_share_path_->SharePath(
651 "error-vm-could-not-be-started", share_path_, PERSIST_YES,
652 base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
653 base::Unretained(this), "error-vm-could-not-be-started",
654 Persist::YES, SeneschalClientCalled::NO, nullptr, "",
655 Success::NO, "VM could not be started"));
656 run_loop()->Run();
657 }
658
TEST_F(GuestOsSharePathTest,SharePersistedPaths)659 TEST_F(GuestOsSharePathTest, SharePersistedPaths) {
660 SetUpVolume();
661 base::FilePath share_path2_ = root_.AppendASCII("path-to-share-2");
662 ASSERT_TRUE(base::CreateDirectory(share_path2_));
663 crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
664 crostini::kCrostiniDefaultVmName);
665 base::Value shared_paths(base::Value::Type::DICTIONARY);
666 base::Value vms(base::Value::Type::LIST);
667 vms.Append(base::Value(crostini::kCrostiniDefaultVmName));
668 shared_paths.SetKey(share_path_.value(), std::move(vms));
669 base::Value vms2(base::Value::Type::LIST);
670 vms2.Append(base::Value(crostini::kCrostiniDefaultVmName));
671 shared_paths.SetKey(share_path2_.value(), std::move(vms2));
672 profile()->GetPrefs()->Set(prefs::kGuestOSPathsSharedToVms, shared_paths);
673 guest_os_share_path_->SharePersistedPaths(
674 crostini::kCrostiniDefaultVmName,
675 base::BindOnce(&GuestOsSharePathTest::SharePersistedPathsCallback,
676 base::Unretained(this)));
677 run_loop()->Run();
678 }
679
TEST_F(GuestOsSharePathTest,RegisterPersistedPaths)680 TEST_F(GuestOsSharePathTest, RegisterPersistedPaths) {
681 base::Value shared_paths(base::Value::Type::DICTIONARY);
682 SetUpVolume();
683 profile()->GetPrefs()->Set(prefs::kGuestOSPathsSharedToVms, shared_paths);
684
685 guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a/a"));
686 const base::DictionaryValue* prefs =
687 profile()->GetPrefs()->GetDictionary(prefs::kGuestOSPathsSharedToVms);
688 EXPECT_EQ(prefs->size(), 1U);
689 EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList().size(), 1U);
690 EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList()[0].GetString(), "v1");
691
692 // Adding the same path again for same VM should not cause any changes.
693 guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a/a"));
694 prefs = profile()->GetPrefs()->GetDictionary(prefs::kGuestOSPathsSharedToVms);
695 EXPECT_EQ(prefs->size(), 1U);
696 EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList().size(), 1U);
697
698 // Adding the same path for a new VM adds to the vm list.
699 guest_os_share_path_->RegisterPersistedPath("v2", base::FilePath("/a/a/a"));
700 EXPECT_EQ(prefs->size(), 1U);
701 EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList().size(), 2U);
702 EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList()[0].GetString(), "v1");
703 EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList()[1].GetString(), "v2");
704
705 // Add more paths.
706 guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a/b"));
707 guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a/c"));
708 guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/b/a"));
709 guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/b/a/a"));
710 EXPECT_EQ(prefs->size(), 5U);
711 EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList().size(), 2U);
712 EXPECT_EQ(prefs->FindKey("/a/a/b")->GetList().size(), 1U);
713 EXPECT_EQ(prefs->FindKey("/a/a/c")->GetList().size(), 1U);
714 EXPECT_EQ(prefs->FindKey("/a/b/a")->GetList().size(), 1U);
715 EXPECT_EQ(prefs->FindKey("/b/a/a")->GetList().size(), 1U);
716
717 // Adding /a/a should remove /a/a/a, /a/a/b, /a/a/c.
718 guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a"));
719 EXPECT_EQ(prefs->size(), 4U);
720 EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList().size(), 1U);
721 EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList()[0].GetString(), "v2");
722 EXPECT_EQ(prefs->FindKey("/a/b/a")->GetList().size(), 1U);
723 EXPECT_EQ(prefs->FindKey("/b/a/a")->GetList().size(), 1U);
724 EXPECT_EQ(prefs->FindKey("/a/a")->GetList().size(), 1U);
725 EXPECT_EQ(prefs->FindKey("/a/a")->GetList()[0].GetString(), "v1");
726
727 // Adding /a should remove /a/a, /a/b/a.
728 guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a"));
729 EXPECT_EQ(prefs->size(), 3U);
730 EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList().size(), 1U);
731 EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList()[0].GetString(), "v2");
732 EXPECT_EQ(prefs->FindKey("/b/a/a")->GetList().size(), 1U);
733 EXPECT_EQ(prefs->FindKey("/a")->GetList().size(), 1U);
734 EXPECT_EQ(prefs->FindKey("/a")->GetList()[0].GetString(), "v1");
735
736 // Adding / should remove all others.
737 guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/"));
738 EXPECT_EQ(prefs->size(), 2U);
739 EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList().size(), 1U);
740 EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList()[0].GetString(), "v2");
741 EXPECT_EQ(prefs->FindKey("/")->GetList().size(), 1U);
742 EXPECT_EQ(prefs->FindKey("/")->GetList()[0].GetString(), "v1");
743
744 // Add / for v2.
745 guest_os_share_path_->RegisterPersistedPath("v2", base::FilePath("/"));
746 EXPECT_EQ(prefs->size(), 1U);
747 EXPECT_EQ(prefs->FindKey("/")->GetList().size(), 2U);
748 EXPECT_EQ(prefs->FindKey("/")->GetList()[0].GetString(), "v1");
749 EXPECT_EQ(prefs->FindKey("/")->GetList()[1].GetString(), "v2");
750 }
751
TEST_F(GuestOsSharePathTest,UnsharePathSuccess)752 TEST_F(GuestOsSharePathTest, UnsharePathSuccess) {
753 SetUpVolume();
754 DictionaryPrefUpdate update(profile()->GetPrefs(),
755 prefs::kGuestOSPathsSharedToVms);
756 base::DictionaryValue* shared_paths = update.Get();
757 base::Value vms(base::Value::Type::LIST);
758 vms.Append(base::Value("vm-running"));
759 shared_paths->SetKey(shared_path_.value(), std::move(vms));
760 guest_os_share_path_->UnsharePath(
761 "vm-running", shared_path_, true,
762 base::BindOnce(&GuestOsSharePathTest::UnsharePathCallback,
763 base::Unretained(this), shared_path_, Persist::NO,
764 SeneschalClientCalled::YES, "MyFiles/already-shared",
765 Success::YES, ""));
766 run_loop()->Run();
767 }
768
TEST_F(GuestOsSharePathTest,UnsharePathRoot)769 TEST_F(GuestOsSharePathTest, UnsharePathRoot) {
770 SetUpVolume();
771 guest_os_share_path_->UnsharePath(
772 "vm-running", root_, true,
773 base::BindOnce(&GuestOsSharePathTest::UnsharePathCallback,
774 base::Unretained(this), root_, Persist::NO,
775 SeneschalClientCalled::YES, "MyFiles", Success::YES, ""));
776 run_loop()->Run();
777 }
778
TEST_F(GuestOsSharePathTest,UnsharePathVmNotRunning)779 TEST_F(GuestOsSharePathTest, UnsharePathVmNotRunning) {
780 SetUpVolume();
781 DictionaryPrefUpdate update(profile()->GetPrefs(),
782 prefs::kGuestOSPathsSharedToVms);
783 base::DictionaryValue* shared_paths = update.Get();
784 base::Value vms(base::Value::Type::LIST);
785 vms.Append(base::Value("vm-not-running"));
786 shared_paths->SetKey(shared_path_.value(), std::move(vms));
787 guest_os_share_path_->UnsharePath(
788 "vm-not-running", shared_path_, true,
789 base::BindOnce(&GuestOsSharePathTest::UnsharePathCallback,
790 base::Unretained(this), shared_path_, Persist::NO,
791 SeneschalClientCalled::NO, "", Success::YES,
792 "VM not running"));
793 run_loop()->Run();
794 }
795
TEST_F(GuestOsSharePathTest,UnsharePathPluginVmNotRunning)796 TEST_F(GuestOsSharePathTest, UnsharePathPluginVmNotRunning) {
797 SetUpVolume();
798 DictionaryPrefUpdate update(profile()->GetPrefs(),
799 prefs::kGuestOSPathsSharedToVms);
800 base::DictionaryValue* shared_paths = update.Get();
801 base::Value vms(base::Value::Type::LIST);
802 vms.Append(base::Value("PvmDefault"));
803 shared_paths->SetKey(shared_path_.value(), std::move(vms));
804 guest_os_share_path_->UnsharePath(
805 "PvmDefault", shared_path_, true,
806 base::BindOnce(&GuestOsSharePathTest::UnsharePathCallback,
807 base::Unretained(this), shared_path_, Persist::NO,
808 SeneschalClientCalled::NO, "", Success::YES,
809 "PluginVm not running"));
810 run_loop()->Run();
811 }
812
813 // Tests that it cannot unshare path when ARCVM is not running.
TEST_F(GuestOsSharePathTest,UnsharePathArcvmNotRunning)814 TEST_F(GuestOsSharePathTest, UnsharePathArcvmNotRunning) {
815 SetUpVolume();
816 DictionaryPrefUpdate update(profile()->GetPrefs(),
817 prefs::kGuestOSPathsSharedToVms);
818 base::DictionaryValue* shared_paths = update.Get();
819 base::Value vms(base::Value::Type::LIST);
820 vms.Append(base::Value(arc::kArcVmName));
821 shared_paths->SetKey(shared_path_.value(), std::move(vms));
822
823 // Remove VmInfo from |arc_session_manager_| to simulate a stopped ARCVM.
824 vm_tools::concierge::VmStoppedSignal stop_signal;
825 stop_signal.set_name(arc::kArcVmName);
826 arc_session_manager_->OnVmStopped(stop_signal);
827
828 guest_os_share_path_->UnsharePath(
829 arc::kArcVmName, shared_path_, true,
830 base::BindOnce(&GuestOsSharePathTest::UnsharePathCallback,
831 base::Unretained(this), shared_path_, Persist::NO,
832 SeneschalClientCalled::NO, "", Success::YES,
833 "ARCVM not running, cannot unshare paths"));
834 run_loop()->Run();
835 }
836
TEST_F(GuestOsSharePathTest,UnsharePathInvalidPath)837 TEST_F(GuestOsSharePathTest, UnsharePathInvalidPath) {
838 SetUpVolume();
839 base::FilePath invalid("invalid/path");
840 guest_os_share_path_->UnsharePath(
841 "vm-running", invalid, true,
842 base::BindOnce(&GuestOsSharePathTest::UnsharePathCallback,
843 base::Unretained(this), invalid, Persist::NO,
844 SeneschalClientCalled::NO, "", Success::NO,
845 "Invalid path to unshare"));
846 run_loop()->Run();
847 }
848
TEST_F(GuestOsSharePathTest,GetPersistedSharedPaths)849 TEST_F(GuestOsSharePathTest, GetPersistedSharedPaths) {
850 SetUpVolume();
851 // path1:['vm1'], path2:['vm2'], path3:['vm3'], path12:['vm1','vm2']
852 base::Value shared_paths(base::Value::Type::DICTIONARY);
853
854 base::FilePath path1("/path1");
855 base::Value path1vms(base::Value::Type::LIST);
856 path1vms.Append(base::Value("vm1"));
857 shared_paths.SetKey(path1.value(), std::move(path1vms));
858 base::FilePath path2("/path2");
859 base::Value path2vms(base::Value::Type::LIST);
860 path2vms.Append(base::Value("vm2"));
861 shared_paths.SetKey(path2.value(), std::move(path2vms));
862 base::FilePath path3("/path3");
863 base::Value path3vms(base::Value::Type::LIST);
864 path3vms.Append(base::Value("vm3"));
865 shared_paths.SetKey(path3.value(), std::move(path3vms));
866 base::FilePath path12("/path12");
867 base::Value path12vms(base::Value::Type::LIST);
868 path12vms.Append(base::Value("vm1"));
869 path12vms.Append(base::Value("vm2"));
870 shared_paths.SetKey(path12.value(), std::move(path12vms));
871 profile()->GetPrefs()->Set(prefs::kGuestOSPathsSharedToVms, shared_paths);
872
873 std::vector<base::FilePath> paths =
874 guest_os_share_path_->GetPersistedSharedPaths("vm1");
875 std::sort(paths.begin(), paths.end());
876 EXPECT_EQ(paths.size(), 2U);
877 EXPECT_EQ(paths[0], path1);
878 EXPECT_EQ(paths[1], path12);
879
880 paths = guest_os_share_path_->GetPersistedSharedPaths("vm2");
881 std::sort(paths.begin(), paths.end());
882 EXPECT_EQ(paths.size(), 2U);
883 EXPECT_EQ(paths[0], path12);
884 EXPECT_EQ(paths[1], path2);
885
886 paths = guest_os_share_path_->GetPersistedSharedPaths("vm3");
887 EXPECT_EQ(paths.size(), 1U);
888 EXPECT_EQ(paths[0], path3);
889
890 paths = guest_os_share_path_->GetPersistedSharedPaths("vm4");
891 EXPECT_EQ(paths.size(), 0U);
892 }
893
TEST_F(GuestOsSharePathTest,ShareOnMountSuccessParentMount)894 TEST_F(GuestOsSharePathTest, ShareOnMountSuccessParentMount) {
895 SetUpVolume();
896 crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
897 crostini::kCrostiniDefaultVmName);
898 guest_os_share_path_->set_seneschal_callback_for_testing(base::BindRepeating(
899 &GuestOsSharePathTest::SeneschalSharePathCallback, base::Unretained(this),
900 "share-on-mount", shared_path_, crostini::kCrostiniDefaultVmName,
901 Persist::NO, SeneschalClientCalled::YES,
902 &vm_tools::seneschal::SharePathRequest::MY_FILES, "already-shared",
903 Success::YES, ""));
904 guest_os_share_path_->OnVolumeMounted(chromeos::MountError::MOUNT_ERROR_NONE,
905 *volume_downloads_);
906 run_loop_->Run();
907 }
908
TEST_F(GuestOsSharePathTest,ShareOnMountSuccessSelfMount)909 TEST_F(GuestOsSharePathTest, ShareOnMountSuccessSelfMount) {
910 SetUpVolume();
911 crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
912 crostini::kCrostiniDefaultVmName);
913 auto volume_shared_path =
914 file_manager::Volume::CreateForDownloads(shared_path_);
915 guest_os_share_path_->set_seneschal_callback_for_testing(base::BindRepeating(
916 &GuestOsSharePathTest::SeneschalSharePathCallback, base::Unretained(this),
917 "share-on-mount", shared_path_, crostini::kCrostiniDefaultVmName,
918 Persist::NO, SeneschalClientCalled::YES,
919 &vm_tools::seneschal::SharePathRequest::MY_FILES, "already-shared",
920 Success::YES, ""));
921 guest_os_share_path_->OnVolumeMounted(chromeos::MountError::MOUNT_ERROR_NONE,
922 *volume_shared_path);
923 run_loop()->Run();
924 }
925
TEST_F(GuestOsSharePathTest,ShareOnMountVmNotRunning)926 TEST_F(GuestOsSharePathTest, ShareOnMountVmNotRunning) {
927 SetUpVolume();
928
929 // Test mount.
930 guest_os_share_path_->OnVolumeMounted(chromeos::MountError::MOUNT_ERROR_NONE,
931 *volume_downloads_);
932 EXPECT_EQ(fake_seneschal_client_->share_path_called(), false);
933
934 // Test unmount.
935 guest_os_share_path_->OnVolumeUnmounted(
936 chromeos::MountError::MOUNT_ERROR_NONE, *volume_downloads_);
937 EXPECT_EQ(fake_seneschal_client_->share_path_called(), false);
938 }
939
TEST_F(GuestOsSharePathTest,ShareOnMountVolumeUnrelated)940 TEST_F(GuestOsSharePathTest, ShareOnMountVolumeUnrelated) {
941 SetUpVolume();
942 auto volume_unrelated_ = file_manager::Volume::CreateForDownloads(
943 base::FilePath("/unrelated/path"));
944
945 // Test mount.
946 guest_os_share_path_->OnVolumeMounted(chromeos::MountError::MOUNT_ERROR_NONE,
947 *volume_unrelated_);
948 EXPECT_EQ(fake_seneschal_client_->share_path_called(), false);
949
950 // Test unmount.
951 guest_os_share_path_->OnVolumeUnmounted(
952 chromeos::MountError::MOUNT_ERROR_NONE, *volume_unrelated_);
953 EXPECT_EQ(fake_seneschal_client_->share_path_called(), false);
954 }
955
TEST_F(GuestOsSharePathTest,UnshareOnUnmountSuccessParentMount)956 TEST_F(GuestOsSharePathTest, UnshareOnUnmountSuccessParentMount) {
957 SetUpVolume();
958 crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
959 crostini::kCrostiniDefaultVmName);
960 guest_os_share_path_->set_seneschal_callback_for_testing(base::BindRepeating(
961 &GuestOsSharePathTest::SeneschalUnsharePathCallback,
962 base::Unretained(this), "unshare-on-unmount", shared_path_, Persist::YES,
963 SeneschalClientCalled::YES, "MyFiles/already-shared", Success::YES, ""));
964 guest_os_share_path_->OnVolumeUnmounted(
965 chromeos::MountError::MOUNT_ERROR_NONE, *volume_downloads_);
966 run_loop()->Run();
967 }
968
TEST_F(GuestOsSharePathTest,UnshareOnUnmountSuccessSelfMount)969 TEST_F(GuestOsSharePathTest, UnshareOnUnmountSuccessSelfMount) {
970 SetUpVolume();
971 crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
972 crostini::kCrostiniDefaultVmName);
973 auto volume_shared_path =
974 file_manager::Volume::CreateForDownloads(shared_path_);
975 guest_os_share_path_->set_seneschal_callback_for_testing(base::BindRepeating(
976 &GuestOsSharePathTest::SeneschalUnsharePathCallback,
977 base::Unretained(this), "unshare-on-unmount", shared_path_, Persist::YES,
978 SeneschalClientCalled::YES, "MyFiles/already-shared", Success::YES, ""));
979 guest_os_share_path_->OnVolumeUnmounted(
980 chromeos::MountError::MOUNT_ERROR_NONE, *volume_shared_path);
981 run_loop()->Run();
982 }
983
TEST_F(GuestOsSharePathTest,UnshareOnDeleteMountExists)984 TEST_F(GuestOsSharePathTest, UnshareOnDeleteMountExists) {
985 SetUpVolume();
986 crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
987 crostini::kCrostiniDefaultVmName);
988 ASSERT_TRUE(base::DeleteFile(shared_path_));
989 guest_os_share_path_->set_seneschal_callback_for_testing(base::BindRepeating(
990 &GuestOsSharePathTest::SeneschalUnsharePathCallback,
991 base::Unretained(this), "unshare-on-delete", shared_path_, Persist::NO,
992 SeneschalClientCalled::YES, "MyFiles/already-shared", Success::YES, ""));
993 run_loop()->Run();
994 }
995
TEST_F(GuestOsSharePathTest,UnshareOnDeleteMountRemoved)996 TEST_F(GuestOsSharePathTest, UnshareOnDeleteMountRemoved) {
997 SetUpVolume();
998 crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
999 crostini::kCrostiniDefaultVmName);
1000 // Rename root_ rather than delete to mimic atomic removal of mount.
1001 base::FilePath renamed =
1002 root_.DirName().Append(root_.BaseName().value() + ".tmp");
1003 ASSERT_TRUE(base::Move(root_, renamed));
1004 guest_os_share_path_->set_seneschal_callback_for_testing(base::BindRepeating(
1005 &GuestOsSharePathTest::SeneschalUnsharePathCallback,
1006 base::Unretained(this), "ignore-delete-before-unmount", shared_path_,
1007 Persist::YES, SeneschalClientCalled::NO, "MyFiles/already-shared",
1008 Success::YES, ""));
1009 run_loop()->Run();
1010 }
1011
TEST_F(GuestOsSharePathTest,RegisterPathThenUnshare)1012 TEST_F(GuestOsSharePathTest, RegisterPathThenUnshare) {
1013 SetUpVolume();
1014 crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
1015 crostini::kCrostiniDefaultVmName);
1016 guest_os_share_path_->RegisterSharedPath(crostini::kCrostiniDefaultVmName,
1017 share_path_);
1018 guest_os_share_path_->UnsharePath(
1019 crostini::kCrostiniDefaultVmName, share_path_, true,
1020 base::BindOnce(&GuestOsSharePathTest::UnsharePathCallback,
1021 base::Unretained(this), share_path_, Persist::NO,
1022 SeneschalClientCalled::YES, "MyFiles/path-to-share",
1023 Success::YES, ""));
1024 run_loop()->Run();
1025 }
1026
TEST_F(GuestOsSharePathTest,IsPathShared)1027 TEST_F(GuestOsSharePathTest, IsPathShared) {
1028 SetUpVolume();
1029 // shared_path_ and children paths are shared for 'termina'.
1030 for (auto& path : {shared_path_, shared_path_.Append("a.txt"),
1031 shared_path_.Append("a"), shared_path_.Append("a/b")}) {
1032 EXPECT_TRUE(guest_os_share_path_->IsPathShared(
1033 crostini::kCrostiniDefaultVmName, path));
1034 }
1035 // Any parent paths are not shared.
1036 for (auto& path : {shared_path_.DirName(), root_}) {
1037 EXPECT_FALSE(guest_os_share_path_->IsPathShared(
1038 crostini::kCrostiniDefaultVmName, path));
1039 }
1040
1041 // No paths are shared for 'not-shared' VM.
1042 for (auto& path :
1043 {shared_path_, shared_path_.Append("a.txt"), shared_path_.Append("a"),
1044 shared_path_.Append("a/b"), shared_path_.DirName(), root_}) {
1045 EXPECT_FALSE(guest_os_share_path_->IsPathShared("not-shared", path));
1046 }
1047 }
1048
1049 } // namespace guest_os
1050