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