123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/components/smbfs/smbfs_mounter.h"
- #include <string.h>
- #include <memory>
- #include <tuple>
- #include <utility>
- #include "ash/components/disks/disk_mount_manager.h"
- #include "ash/components/disks/mock_disk_mount_manager.h"
- #include "base/bind.h"
- #include "base/files/file_util.h"
- #include "base/run_loop.h"
- #include "base/test/bind.h"
- #include "base/test/gmock_callback_support.h"
- #include "base/test/multiprocess_test.h"
- #include "base/test/task_environment.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- #include "base/threading/thread.h"
- #include "chromeos/components/mojo_bootstrap/pending_connection_manager.h"
- #include "mojo/core/embedder/scoped_ipc_support.h"
- #include "mojo/public/cpp/bindings/receiver.h"
- #include "mojo/public/cpp/platform/platform_channel.h"
- #include "mojo/public/cpp/system/invitation.h"
- #include "mojo/public/cpp/system/platform_handle.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "testing/multiprocess_func_list.h"
- using testing::_;
- using testing::StartsWith;
- using testing::WithArgs;
- namespace smbfs {
- namespace {
- constexpr char kMountUrlPrefix[] = "smbfs://";
- constexpr char kSharePath[] = "smb://server/share";
- constexpr char kMountDir[] = "bar";
- constexpr base::FilePath::CharType kMountPath[] = FILE_PATH_LITERAL("/foo/bar");
- constexpr int kChildInvitationFd = 42;
- constexpr char kUsername[] = "username";
- constexpr char kWorkgroup[] = "example.com";
- constexpr char kPassword[] = "myverysecurepassword";
- constexpr char kKerberosIdentity[] = "my-kerberos-identity";
- constexpr char kAccountHash[] = "00112233445566778899aabb";
- ash::disks::DiskMountManager::MountPoint MakeMountPointInfo(
- const std::string& source_path,
- const std::string& mount_path) {
- return {source_path, mount_path, ash::MountType::kNetworkStorage};
- }
- class MockDelegate : public SmbFsHost::Delegate {
- public:
- MOCK_METHOD(void, OnDisconnected, (), (override));
- MOCK_METHOD(void,
- RequestCredentials,
- (RequestCredentialsCallback),
- (override));
- };
- class TestSmbFsBootstrapImpl : public mojom::SmbFsBootstrap {
- public:
- MOCK_METHOD(void,
- MountShare,
- (mojom::MountOptionsPtr,
- mojo::PendingRemote<mojom::SmbFsDelegate>,
- MountShareCallback),
- (override));
- };
- class TestSmbFsImpl : public mojom::SmbFs {
- public:
- MOCK_METHOD(void,
- RemoveSavedCredentials,
- (RemoveSavedCredentialsCallback),
- (override));
- MOCK_METHOD(void,
- DeleteRecursively,
- (const base::FilePath&, DeleteRecursivelyCallback),
- (override));
- };
- class TestSmbFsMounter : public SmbFsMounter {
- public:
- TestSmbFsMounter(const std::string& share_path,
- const MountOptions& options,
- SmbFsHost::Delegate* delegate,
- const base::FilePath& mount_path,
- ash::MountError mount_error,
- mojo::Remote<mojom::SmbFsBootstrap> bootstrap)
- : SmbFsMounter(share_path,
- kMountDir,
- options,
- delegate,
- &mock_disk_mount_manager_,
- std::move(bootstrap)) {
- EXPECT_CALL(mock_disk_mount_manager_, MountPath(StartsWith(kMountUrlPrefix),
- _, kMountDir, _, _, _, _))
- .WillOnce(WithArgs<0, 6>(
- [mount_error, mount_path](
- const std::string& source_path,
- ash::disks::DiskMountManager::MountPathCallback callback) {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(
- std::move(callback), mount_error,
- MakeMountPointInfo(source_path, mount_path.value())));
- }));
- if (mount_error == ash::MountError::kNone) {
- EXPECT_CALL(mock_disk_mount_manager_, UnmountPath(mount_path.value(), _))
- .WillOnce(base::test::RunOnceCallback<1>(ash::MountError::kNone));
- } else {
- EXPECT_CALL(mock_disk_mount_manager_, UnmountPath(mount_path.value(), _))
- .Times(0);
- }
- }
- private:
- ash::disks::MockDiskMountManager mock_disk_mount_manager_;
- };
- class SmbFsMounterTest : public testing::Test {
- public:
- void PostMountEvent(
- const std::string& source_path,
- const std::string& mount_path,
- ash::MountError mount_error,
- ash::disks::DiskMountManager::MountPathCallback callback) {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback), mount_error,
- MakeMountPointInfo(source_path, mount_path)));
- }
- protected:
- base::test::TaskEnvironment task_environment_{
- base::test::TaskEnvironment::TimeSource::MOCK_TIME};
- MockDelegate mock_delegate_;
- ash::disks::MockDiskMountManager mock_disk_mount_manager_;
- };
- TEST_F(SmbFsMounterTest, FilesystemMountTimeout) {
- base::RunLoop run_loop;
- auto callback =
- base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
- std::unique_ptr<SmbFsHost> host) {
- EXPECT_EQ(mount_error, mojom::MountError::kTimeout);
- EXPECT_FALSE(host);
- run_loop.Quit();
- });
- std::unique_ptr<SmbFsMounter> mounter = std::make_unique<SmbFsMounter>(
- kSharePath, kMountDir, SmbFsMounter::MountOptions(), &mock_delegate_,
- &mock_disk_mount_manager_);
- EXPECT_CALL(mock_disk_mount_manager_,
- MountPath(StartsWith(kMountUrlPrefix), _, kMountDir, _, _, _, _))
- .Times(1);
- EXPECT_CALL(mock_disk_mount_manager_, UnmountPath(_, _)).Times(0);
- base::TimeTicks start_time = task_environment_.NowTicks();
- mounter->Mount(callback);
- // TaskEnvironment will automatically advance mock time to the next posted
- // task, which is the mount timeout in this case.
- run_loop.Run();
- EXPECT_GE(task_environment_.NowTicks() - start_time, base::Seconds(20));
- }
- TEST_F(SmbFsMounterTest, FilesystemMountFailure) {
- base::RunLoop run_loop;
- auto callback =
- base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
- std::unique_ptr<SmbFsHost> host) {
- EXPECT_EQ(mount_error, mojom::MountError::kUnknown);
- EXPECT_FALSE(host);
- run_loop.Quit();
- });
- std::unique_ptr<SmbFsMounter> mounter = std::make_unique<SmbFsMounter>(
- kSharePath, kMountDir, SmbFsMounter::MountOptions(), &mock_delegate_,
- &mock_disk_mount_manager_);
- EXPECT_CALL(mock_disk_mount_manager_,
- MountPath(StartsWith(kMountUrlPrefix), _, kMountDir, _, _, _, _))
- .WillOnce(WithArgs<0, 6>(
- [this](const std::string& source_path,
- ash::disks::DiskMountManager::MountPathCallback callback) {
- PostMountEvent(source_path, kMountPath, ash::MountError::kInternal,
- std::move(callback));
- }));
- EXPECT_CALL(mock_disk_mount_manager_, UnmountPath(_, _)).Times(0);
- mounter->Mount(callback);
- run_loop.Run();
- }
- TEST_F(SmbFsMounterTest, TimeoutAfterFilesystemMount) {
- base::RunLoop run_loop;
- auto callback =
- base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
- std::unique_ptr<SmbFsHost> host) {
- EXPECT_EQ(mount_error, mojom::MountError::kTimeout);
- EXPECT_FALSE(host);
- run_loop.Quit();
- });
- std::unique_ptr<SmbFsMounter> mounter = std::make_unique<SmbFsMounter>(
- kSharePath, kMountDir, SmbFsMounter::MountOptions(), &mock_delegate_,
- &mock_disk_mount_manager_);
- EXPECT_CALL(mock_disk_mount_manager_,
- MountPath(StartsWith(kMountUrlPrefix), _, kMountDir, _, _, _, _))
- .WillOnce(WithArgs<0, 6>(
- [this](const std::string& source_path,
- ash::disks::DiskMountManager::MountPathCallback callback) {
- PostMountEvent(source_path, kMountPath, ash::MountError::kNone,
- std::move(callback));
- }));
- // Destructing SmbFsMounter on failure will cause the mount point to be
- // unmounted.
- EXPECT_CALL(mock_disk_mount_manager_, UnmountPath(kMountPath, _)).Times(1);
- base::TimeTicks start_time = task_environment_.NowTicks();
- mounter->Mount(callback);
- // TaskEnvironment will automatically advance mock time to the next posted
- // task, which is the mount timeout in this case.
- run_loop.Run();
- EXPECT_GE(task_environment_.NowTicks() - start_time, base::Seconds(20));
- }
- TEST_F(SmbFsMounterTest, FilesystemMountAfterDestruction) {
- base::RunLoop run_loop;
- auto callback = base::BindLambdaForTesting(
- [](mojom::MountError mount_error, std::unique_ptr<SmbFsHost> host) {
- FAIL() << "Callback should not be run";
- });
- std::unique_ptr<SmbFsMounter> mounter = std::make_unique<SmbFsMounter>(
- kSharePath, kMountDir, SmbFsMounter::MountOptions(), &mock_delegate_,
- &mock_disk_mount_manager_);
- EXPECT_CALL(mock_disk_mount_manager_,
- MountPath(StartsWith(kMountUrlPrefix), _, kMountDir, _, _, _, _))
- .WillOnce(WithArgs<0, 6>(
- [this](const std::string& source_path,
- ash::disks::DiskMountManager::MountPathCallback callback) {
- // This posts a mount event to the task queue, which will not be run
- // until |run_loop| is started.
- PostMountEvent(source_path, kMountPath, ash::MountError::kInternal,
- std::move(callback));
- }));
- EXPECT_CALL(mock_disk_mount_manager_, UnmountPath(_, _)).Times(0);
- mounter->Mount(callback);
- // Delete the mounter. Callback should not be run.
- mounter.reset();
- run_loop.RunUntilIdle();
- }
- TEST_F(SmbFsMounterTest, MountOptions) {
- base::RunLoop run_loop;
- auto callback =
- base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
- std::unique_ptr<SmbFsHost> host) {
- EXPECT_EQ(mount_error, mojom::MountError::kOk);
- ASSERT_TRUE(host);
- EXPECT_EQ(host->mount_path(), base::FilePath(kMountPath));
- run_loop.Quit();
- });
- // Dummy Mojo bindings to satifsy lifetimes.
- mojo::PendingRemote<mojom::SmbFsDelegate> delegate_remote;
- TestSmbFsImpl mock_smbfs;
- mojo::Receiver<mojom::SmbFs> smbfs_receiver(&mock_smbfs);
- TestSmbFsBootstrapImpl mock_bootstrap;
- mojo::Receiver<mojom::SmbFsBootstrap> bootstrap_receiver(&mock_bootstrap);
- EXPECT_CALL(mock_bootstrap, MountShare(_, _, _))
- .WillOnce([&delegate_remote, &smbfs_receiver](
- mojom::MountOptionsPtr options,
- mojo::PendingRemote<mojom::SmbFsDelegate> delegate,
- mojom::SmbFsBootstrap::MountShareCallback callback) {
- EXPECT_EQ(options->share_path, kSharePath);
- EXPECT_EQ(options->username, kUsername);
- EXPECT_EQ(options->workgroup, kWorkgroup);
- ASSERT_TRUE(options->password);
- EXPECT_EQ(options->password->length,
- static_cast<int32_t>(strlen(kPassword)));
- std::string password_buf(options->password->length, 'a');
- base::ScopedFD fd =
- mojo::UnwrapPlatformHandle(std::move(options->password->fd))
- .TakeFD();
- EXPECT_TRUE(base::ReadFromFD(fd.get(), &(password_buf.front()),
- options->password->length));
- EXPECT_EQ(password_buf, kPassword);
- EXPECT_TRUE(options->allow_ntlm);
- EXPECT_FALSE(options->skip_connect);
- EXPECT_EQ(options->resolved_host, net::IPAddress(1, 2, 3, 4));
- EXPECT_FALSE(options->credential_storage_options);
- delegate_remote = std::move(delegate);
- std::move(callback).Run(mojom::MountError::kOk,
- smbfs_receiver.BindNewPipeAndPassRemote());
- });
- SmbFsMounter::MountOptions mount_options;
- mount_options.username = kUsername;
- mount_options.workgroup = kWorkgroup;
- mount_options.password = kPassword;
- mount_options.allow_ntlm = true;
- mount_options.resolved_host = net::IPAddress(1, 2, 3, 4);
- std::unique_ptr<SmbFsMounter> mounter = std::make_unique<TestSmbFsMounter>(
- kSharePath, mount_options, &mock_delegate_, base::FilePath(kMountPath),
- ash::MountError::kNone,
- mojo::Remote<mojom::SmbFsBootstrap>(
- bootstrap_receiver.BindNewPipeAndPassRemote()));
- mounter->Mount(callback);
- run_loop.Run();
- }
- TEST_F(SmbFsMounterTest, MountOptions_SkipConnect) {
- base::RunLoop run_loop;
- auto callback =
- base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
- std::unique_ptr<SmbFsHost> host) {
- EXPECT_EQ(mount_error, mojom::MountError::kOk);
- ASSERT_TRUE(host);
- EXPECT_EQ(host->mount_path(), base::FilePath(kMountPath));
- run_loop.Quit();
- });
- // Dummy Mojo bindings to satisfy lifetimes.
- mojo::PendingRemote<mojom::SmbFsDelegate> delegate_remote;
- TestSmbFsImpl mock_smbfs;
- mojo::Receiver<mojom::SmbFs> smbfs_receiver(&mock_smbfs);
- TestSmbFsBootstrapImpl mock_bootstrap;
- mojo::Receiver<mojom::SmbFsBootstrap> bootstrap_receiver(&mock_bootstrap);
- EXPECT_CALL(mock_bootstrap, MountShare(_, _, _))
- .WillOnce([&delegate_remote, &smbfs_receiver](
- mojom::MountOptionsPtr options,
- mojo::PendingRemote<mojom::SmbFsDelegate> delegate,
- mojom::SmbFsBootstrap::MountShareCallback callback) {
- EXPECT_EQ(options->share_path, kSharePath);
- EXPECT_TRUE(options->skip_connect);
- delegate_remote = std::move(delegate);
- std::move(callback).Run(mojom::MountError::kOk,
- smbfs_receiver.BindNewPipeAndPassRemote());
- });
- SmbFsMounter::MountOptions mount_options;
- mount_options.skip_connect = true;
- std::unique_ptr<SmbFsMounter> mounter = std::make_unique<TestSmbFsMounter>(
- kSharePath, mount_options, &mock_delegate_, base::FilePath(kMountPath),
- ash::MountError::kNone,
- mojo::Remote<mojom::SmbFsBootstrap>(
- bootstrap_receiver.BindNewPipeAndPassRemote()));
- mounter->Mount(callback);
- run_loop.Run();
- }
- TEST_F(SmbFsMounterTest, MountOptions_SavePassword) {
- // Salt must be at least 16 bytes.
- const std::vector<uint8_t> kSalt = {0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15};
- base::RunLoop run_loop;
- auto callback =
- base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
- std::unique_ptr<SmbFsHost> host) {
- EXPECT_EQ(mount_error, mojom::MountError::kOk);
- ASSERT_TRUE(host);
- EXPECT_EQ(host->mount_path(), base::FilePath(kMountPath));
- run_loop.Quit();
- });
- // Dummy Mojo bindings to satisfy lifetimes.
- mojo::PendingRemote<mojom::SmbFsDelegate> delegate_remote;
- TestSmbFsImpl mock_smbfs;
- mojo::Receiver<mojom::SmbFs> smbfs_receiver(&mock_smbfs);
- TestSmbFsBootstrapImpl mock_bootstrap;
- mojo::Receiver<mojom::SmbFsBootstrap> bootstrap_receiver(&mock_bootstrap);
- EXPECT_CALL(mock_bootstrap, MountShare(_, _, _))
- .WillOnce([&delegate_remote, &smbfs_receiver, kSalt](
- mojom::MountOptionsPtr options,
- mojo::PendingRemote<mojom::SmbFsDelegate> delegate,
- mojom::SmbFsBootstrap::MountShareCallback callback) {
- EXPECT_EQ(options->share_path, kSharePath);
- ASSERT_TRUE(options->credential_storage_options);
- EXPECT_EQ(options->credential_storage_options->account_hash,
- kAccountHash);
- EXPECT_EQ(options->credential_storage_options->salt, kSalt);
- delegate_remote = std::move(delegate);
- std::move(callback).Run(mojom::MountError::kOk,
- smbfs_receiver.BindNewPipeAndPassRemote());
- });
- SmbFsMounter::MountOptions mount_options;
- mount_options.save_restore_password = true;
- mount_options.account_hash = kAccountHash;
- mount_options.password_salt = kSalt;
- std::unique_ptr<SmbFsMounter> mounter = std::make_unique<TestSmbFsMounter>(
- kSharePath, mount_options, &mock_delegate_, base::FilePath(kMountPath),
- ash::MountError::kNone,
- mojo::Remote<mojom::SmbFsBootstrap>(
- bootstrap_receiver.BindNewPipeAndPassRemote()));
- mounter->Mount(callback);
- run_loop.Run();
- }
- TEST_F(SmbFsMounterTest, KerberosAuthentication) {
- base::RunLoop run_loop;
- auto callback =
- base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
- std::unique_ptr<SmbFsHost> host) {
- EXPECT_EQ(mount_error, mojom::MountError::kOk);
- ASSERT_TRUE(host);
- EXPECT_EQ(host->mount_path(), base::FilePath(kMountPath));
- run_loop.Quit();
- });
- // Dummy Mojo bindings to satifsy lifetimes.
- mojo::PendingRemote<mojom::SmbFsDelegate> delegate_remote;
- TestSmbFsImpl mock_smbfs;
- mojo::Receiver<mojom::SmbFs> smbfs_receiver(&mock_smbfs);
- TestSmbFsBootstrapImpl mock_bootstrap;
- mojo::Receiver<mojom::SmbFsBootstrap> bootstrap_receiver(&mock_bootstrap);
- EXPECT_CALL(mock_bootstrap, MountShare(_, _, _))
- .WillOnce([&delegate_remote, &smbfs_receiver](
- mojom::MountOptionsPtr options,
- mojo::PendingRemote<mojom::SmbFsDelegate> delegate,
- mojom::SmbFsBootstrap::MountShareCallback callback) {
- EXPECT_EQ(options->share_path, kSharePath);
- EXPECT_EQ(options->username, kUsername);
- EXPECT_EQ(options->workgroup, kWorkgroup);
- EXPECT_FALSE(options->allow_ntlm);
- EXPECT_FALSE(options->password);
- ASSERT_TRUE(options->kerberos_config);
- EXPECT_EQ(options->kerberos_config->source,
- mojom::KerberosConfig::Source::kKerberos);
- EXPECT_EQ(options->kerberos_config->identity, kKerberosIdentity);
- delegate_remote = std::move(delegate);
- std::move(callback).Run(mojom::MountError::kOk,
- smbfs_receiver.BindNewPipeAndPassRemote());
- });
- SmbFsMounter::MountOptions mount_options;
- mount_options.username = kUsername;
- mount_options.workgroup = kWorkgroup;
- // Even though the password is set, it should be ignored because kerberos
- // authentication is being used.
- mount_options.password = kPassword;
- mount_options.kerberos_options =
- absl::make_optional<SmbFsMounter::KerberosOptions>(
- SmbFsMounter::KerberosOptions::Source::kKerberos, kKerberosIdentity);
- std::unique_ptr<SmbFsMounter> mounter = std::make_unique<TestSmbFsMounter>(
- kSharePath, mount_options, &mock_delegate_, base::FilePath(kMountPath),
- ash::MountError::kNone,
- mojo::Remote<mojom::SmbFsBootstrap>(
- bootstrap_receiver.BindNewPipeAndPassRemote()));
- mounter->Mount(callback);
- run_loop.Run();
- }
- TEST_F(SmbFsMounterTest, BootstrapMountError) {
- base::RunLoop run_loop;
- auto callback =
- base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
- std::unique_ptr<SmbFsHost> host) {
- EXPECT_EQ(mount_error, mojom::MountError::kAccessDenied);
- EXPECT_FALSE(host);
- run_loop.Quit();
- });
- // Dummy Mojo bindings to satifsy lifetimes.
- mojo::PendingRemote<mojom::SmbFsDelegate> delegate_remote;
- TestSmbFsBootstrapImpl mock_bootstrap;
- mojo::Receiver<mojom::SmbFsBootstrap> bootstrap_receiver(&mock_bootstrap);
- EXPECT_CALL(mock_bootstrap, MountShare(_, _, _))
- .WillOnce([&delegate_remote](
- mojom::MountOptionsPtr options,
- mojo::PendingRemote<mojom::SmbFsDelegate> delegate,
- mojom::SmbFsBootstrap::MountShareCallback callback) {
- delegate_remote = std::move(delegate);
- std::move(callback).Run(mojom::MountError::kAccessDenied, {});
- });
- std::unique_ptr<SmbFsMounter> mounter = std::make_unique<TestSmbFsMounter>(
- kSharePath, SmbFsMounter::MountOptions(), &mock_delegate_,
- base::FilePath(kMountPath), ash::MountError::kNone,
- mojo::Remote<mojom::SmbFsBootstrap>(
- bootstrap_receiver.BindNewPipeAndPassRemote()));
- mounter->Mount(callback);
- run_loop.Run();
- }
- TEST_F(SmbFsMounterTest, BootstrapDisconnection) {
- base::RunLoop run_loop;
- auto callback =
- base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
- std::unique_ptr<SmbFsHost> host) {
- EXPECT_EQ(mount_error, mojom::MountError::kUnknown);
- EXPECT_FALSE(host);
- run_loop.Quit();
- });
- // Dummy Mojo bindings to satifsy lifetimes.
- mojo::PendingRemote<mojom::SmbFsDelegate> delegate_remote;
- TestSmbFsBootstrapImpl mock_bootstrap;
- mojo::Receiver<mojom::SmbFsBootstrap> bootstrap_receiver(&mock_bootstrap);
- EXPECT_CALL(mock_bootstrap, MountShare(_, _, _))
- .WillOnce([&bootstrap_receiver](
- mojom::MountOptionsPtr options,
- mojo::PendingRemote<mojom::SmbFsDelegate> delegate,
- mojom::SmbFsBootstrap::MountShareCallback callback) {
- // Reset the bootstrap binding, which should cause a disconnect event.
- bootstrap_receiver.reset();
- std::move(callback).Run(mojom::MountError::kAccessDenied, {});
- });
- std::unique_ptr<SmbFsMounter> mounter = std::make_unique<TestSmbFsMounter>(
- kSharePath, SmbFsMounter::MountOptions(), &mock_delegate_,
- base::FilePath(kMountPath), ash::MountError::kNone,
- mojo::Remote<mojom::SmbFsBootstrap>(
- bootstrap_receiver.BindNewPipeAndPassRemote()));
- mounter->Mount(callback);
- run_loop.Run();
- }
- class SmbFsMounterE2eTest : public testing::Test {
- public:
- void PostMountEvent(
- const std::string& source_path,
- const std::string& mount_path,
- ash::disks::DiskMountManager::MountPathCallback callback) {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback), ash::MountError::kNone,
- MakeMountPointInfo(source_path, mount_path)));
- }
- protected:
- // This test performs actual IPC using sockets, and therefore cannot use
- // MOCK_TIME, which automatically advances time when the main loop is idle.
- base::test::TaskEnvironment task_environment_;
- MockDelegate mock_delegate_;
- ash::disks::MockDiskMountManager mock_disk_mount_manager_;
- };
- // Child process that emulates the behaviour of smbfs.
- MULTIPROCESS_TEST_MAIN(SmbFsMain) {
- base::test::TaskEnvironment task_environment(
- base::test::TaskEnvironment::MainThreadType::IO,
- base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED);
- mojo::core::ScopedIPCSupport ipc_support(
- task_environment.GetMainThreadTaskRunner(),
- mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN);
- mojo::IncomingInvitation invitation =
- mojo::IncomingInvitation::Accept(mojo::PlatformChannelEndpoint(
- mojo::PlatformHandle(base::ScopedFD(kChildInvitationFd))));
- TestSmbFsImpl mock_smbfs;
- mojo::Receiver<mojom::SmbFs> smbfs_receiver(&mock_smbfs);
- TestSmbFsBootstrapImpl mock_bootstrap;
- mojo::Receiver<mojom::SmbFsBootstrap> bootstrap_receiver(&mock_bootstrap);
- mojo::PendingRemote<mojom::SmbFsDelegate> delegate_remote;
- base::RunLoop run_loop;
- EXPECT_CALL(mock_bootstrap, MountShare(_, _, _))
- .WillOnce([&smbfs_receiver, &run_loop, &delegate_remote](
- mojom::MountOptionsPtr options,
- mojo::PendingRemote<mojom::SmbFsDelegate> delegate,
- mojom::SmbFsBootstrap::MountShareCallback callback) {
- EXPECT_EQ(options->share_path, kSharePath);
- EXPECT_EQ(options->username, kUsername);
- EXPECT_EQ(options->workgroup, kWorkgroup);
- ASSERT_TRUE(options->password);
- EXPECT_EQ(options->password->length,
- static_cast<int32_t>(strlen(kPassword)));
- std::string password_buf(options->password->length, 'a');
- base::ScopedFD fd =
- mojo::UnwrapPlatformHandle(std::move(options->password->fd))
- .TakeFD();
- EXPECT_TRUE(base::ReadFromFD(fd.get(), &(password_buf.front()),
- options->password->length));
- EXPECT_EQ(password_buf, kPassword);
- EXPECT_FALSE(options->allow_ntlm);
- delegate_remote = std::move(delegate);
- mojo::PendingRemote<mojom::SmbFs> smbfs =
- smbfs_receiver.BindNewPipeAndPassRemote();
- // When the SmbFsHost in the parent is destroyed, this message pipe will
- // be closed and treat that as a signal to shut down.
- smbfs_receiver.set_disconnect_handler(run_loop.QuitClosure());
- std::move(callback).Run(mojom::MountError::kOk, std::move(smbfs));
- });
- bootstrap_receiver.Bind(mojo::PendingReceiver<mojom::SmbFsBootstrap>(
- invitation.ExtractMessagePipe(mojom::kBootstrapPipeName)));
- run_loop.Run();
- return 0;
- }
- TEST_F(SmbFsMounterE2eTest, MountSuccess) {
- mojo::PlatformChannel channel;
- base::LaunchOptions launch_options;
- base::ScopedFD child_fd =
- channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD();
- launch_options.fds_to_remap.push_back(
- std::make_pair(child_fd.get(), kChildInvitationFd));
- base::Process child_process = base::SpawnMultiProcessTestChild(
- "SmbFsMain", base::GetMultiProcessTestChildBaseCommandLine(),
- launch_options);
- ASSERT_TRUE(child_process.IsValid());
- // The child FD has been passed to the child process at this point.
- std::ignore = child_fd.release();
- EXPECT_CALL(mock_disk_mount_manager_,
- MountPath(StartsWith(kMountUrlPrefix), _, kMountDir, _, _, _, _))
- .WillOnce(WithArgs<0,
- 6>([this, &channel](
- const std::string& source_path,
- ash::disks::DiskMountManager::MountPathCallback
- callback) {
- // Emulates cros-disks mount success.
- PostMountEvent(source_path, kMountPath, std::move(callback));
- // Emulates smbfs connecting to the org.chromium.SmbFs D-Bus service and
- // providing a Mojo connection endpoint.
- const std::string token =
- source_path.substr(sizeof(kMountUrlPrefix) - 1);
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindLambdaForTesting([token, &channel]() {
- mojo_bootstrap::PendingConnectionManager::Get().OpenIpcChannel(
- token,
- channel.TakeLocalEndpoint().TakePlatformHandle().TakeFD());
- }));
- }));
- EXPECT_CALL(mock_disk_mount_manager_, UnmountPath(kMountPath, _))
- .WillOnce(base::test::RunOnceCallback<1>(ash::MountError::kNone));
- EXPECT_CALL(mock_delegate_, OnDisconnected()).Times(0);
- base::RunLoop run_loop;
- auto callback =
- base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
- std::unique_ptr<SmbFsHost> host) {
- EXPECT_EQ(mount_error, mojom::MountError::kOk);
- EXPECT_TRUE(host);
- // Don't capture |host|. Its destruction will close the Mojo message
- // pipe and cause the child process to shut down gracefully.
- run_loop.Quit();
- });
- SmbFsMounter::MountOptions mount_options;
- mount_options.username = kUsername;
- mount_options.workgroup = kWorkgroup;
- mount_options.password = kPassword;
- std::unique_ptr<SmbFsMounter> mounter = std::make_unique<SmbFsMounter>(
- kSharePath, kMountDir, mount_options, &mock_delegate_,
- &mock_disk_mount_manager_);
- mounter->Mount(callback);
- run_loop.Run();
- EXPECT_TRUE(child_process.WaitForExit(nullptr));
- }
- } // namespace
- } // namespace smbfs
|