text: // Copyright 2013 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 "chrome/browser/chromeos/fileapi/file_system_backend.h"
  
  #include <set>
  
  #include "base/files/file_path.h"
  #include "base/path_service.h"
  #include "chromeos/dbus/cros_disks_client.h"
  #include "content/public/test/mock_special_storage_policy.h"
  #include "storage/browser/fileapi/external_mount_points.h"
  #include "storage/browser/fileapi/file_system_url.h"
  #include "testing/gtest/include/gtest/gtest.h"
  #include "url/url_util.h"
  
  #define FPL(x) FILE_PATH_LITERAL(x)
  
  using storage::ExternalMountPoints;
  using storage::FileSystemURL;
  
  namespace {
  
  FileSystemURL CreateFileSystemURL(const std::string& extension,
                                    const char* path,
                                    ExternalMountPoints* mount_points) {
    return mount_points->CreateCrackedFileSystemURL(
        GURL("chrome-extension://" + extension + "/"),
        storage::kFileSystemTypeExternal,
        base::FilePath::FromUTF8Unsafe(path));
  }
  
  TEST(ChromeOSFileSystemBackendTest, DefaultMountPoints) {
    // Make sure no system-level mount points are registered before testing
    // to avoid flakiness.
    storage::ExternalMountPoints::GetSystemInstance()->RevokeAllFileSystems();
  
    scoped_refptr<storage::SpecialStoragePolicy> storage_policy =
        new content::MockSpecialStoragePolicy();
    scoped_refptr<storage::ExternalMountPoints> mount_points(
        storage::ExternalMountPoints::CreateRefCounted());
    chromeos::FileSystemBackend backend(
        NULL,  // drive_delegate
        NULL,  // file_system_provider_delegate
        NULL,  // mtp_delegate
        storage_policy,
        mount_points.get(),
        storage::ExternalMountPoints::GetSystemInstance());
    backend.AddSystemMountPoints();
    std::vector<base::FilePath> root_dirs = backend.GetRootDirectories();
    std::set<base::FilePath> root_dirs_set(root_dirs.begin(), root_dirs.end());
  
    // By default there should be 3 mount points (in system mount points):
    EXPECT_EQ(3u, root_dirs.size());
  
    EXPECT_TRUE(root_dirs_set.count(
        chromeos::CrosDisksClient::GetRemovableDiskMountPoint()));
    EXPECT_TRUE(root_dirs_set.count(
        chromeos::CrosDisksClient::GetArchiveMountPoint()));
    EXPECT_TRUE(root_dirs_set.count(base::FilePath(FPL("/usr/share/oem"))));
  }
  
  TEST(ChromeOSFileSystemBackendTest, GetRootDirectories) {
    scoped_refptr<storage::SpecialStoragePolicy> storage_policy =
        new content::MockSpecialStoragePolicy();
    scoped_refptr<storage::ExternalMountPoints> mount_points(
        storage::ExternalMountPoints::CreateRefCounted());
  
    scoped_refptr<storage::ExternalMountPoints> system_mount_points(
        storage::ExternalMountPoints::CreateRefCounted());
  
    chromeos::FileSystemBackend backend(NULL,  // drive_delegate
                                        NULL,  // file_system_provider_delegate
                                        NULL,  // mtp_delegate
                                        storage_policy,
                                        mount_points.get(),
                                        system_mount_points.get());
  
    const size_t initial_root_dirs_size = backend.GetRootDirectories().size();
  
    // Register 'local' test mount points.
    mount_points->RegisterFileSystem("c",
                                     storage::kFileSystemTypeNativeLocal,
                                     storage::FileSystemMountOption(),
                                     base::FilePath(FPL("/a/b/c")));
    mount_points->RegisterFileSystem("d",
                                     storage::kFileSystemTypeNativeLocal,
                                     storage::FileSystemMountOption(),
                                     base::FilePath(FPL("/b/c/d")));
  
    // Register system test mount points.
    system_mount_points->RegisterFileSystem("d",
                                            storage::kFileSystemTypeNativeLocal,
                                            storage::FileSystemMountOption(),
                                            base::FilePath(FPL("/g/c/d")));
    system_mount_points->RegisterFileSystem("e",
                                            storage::kFileSystemTypeNativeLocal,
                                            storage::FileSystemMountOption(),
                                            base::FilePath(FPL("/g/d/e")));
  
    std::vector<base::FilePath> root_dirs = backend.GetRootDirectories();
    std::set<base::FilePath> root_dirs_set(root_dirs.begin(), root_dirs.end());
    EXPECT_EQ(initial_root_dirs_size + 4, root_dirs.size());
    EXPECT_TRUE(root_dirs_set.count(base::FilePath(FPL("/a/b/c"))));
    EXPECT_TRUE(root_dirs_set.count(base::FilePath(FPL("/b/c/d"))));
    EXPECT_TRUE(root_dirs_set.count(base::FilePath(FPL("/g/c/d"))));
    EXPECT_TRUE(root_dirs_set.count(base::FilePath(FPL("/g/d/e"))));
  }
  
  TEST(ChromeOSFileSystemBackendTest, AccessPermissions) {
    url::AddStandardScheme("chrome-extension");
  
    scoped_refptr<content::MockSpecialStoragePolicy> storage_policy =
        new content::MockSpecialStoragePolicy();
    scoped_refptr<storage::ExternalMountPoints> mount_points(
        storage::ExternalMountPoints::CreateRefCounted());
    scoped_refptr<storage::ExternalMountPoints> system_mount_points(
        storage::ExternalMountPoints::CreateRefCounted());
    chromeos::FileSystemBackend backend(NULL,  // drive_delegate
                                        NULL,  // file_system_provider_delegate
                                        NULL,  // mtp_delegate
                                        storage_policy,
                                        mount_points.get(),
                                        system_mount_points.get());
  
    std::string extension("ddammdhioacbehjngdmkjcjbnfginlla");
  
    storage_policy->AddFileHandler(extension);
  
    // Initialize mount points.
    ASSERT_TRUE(system_mount_points->RegisterFileSystem(
        "system",
        storage::kFileSystemTypeNativeLocal,
        storage::FileSystemMountOption(),
        base::FilePath(FPL("/g/system"))));
    ASSERT_TRUE(mount_points->RegisterFileSystem(
        "removable",
        storage::kFileSystemTypeNativeLocal,
        storage::FileSystemMountOption(),
        base::FilePath(FPL("/media/removable"))));
    ASSERT_TRUE(mount_points->RegisterFileSystem(
        "oem",
        storage::kFileSystemTypeRestrictedNativeLocal,
        storage::FileSystemMountOption(),
        base::FilePath(FPL("/usr/share/oem"))));
  
    // Backend specific mount point access.
    EXPECT_FALSE(backend.IsAccessAllowed(
        CreateFileSystemURL(extension, "removable/foo", mount_points.get())));
  
    backend.GrantFileAccessToExtension(extension,
                                        base::FilePath(FPL("removable/foo")));
    EXPECT_TRUE(backend.IsAccessAllowed(
        CreateFileSystemURL(extension, "removable/foo", mount_points.get())));
    EXPECT_FALSE(backend.IsAccessAllowed(
        CreateFileSystemURL(extension, "removable/foo1", mount_points.get())));
  
    // System mount point access.
    EXPECT_FALSE(backend.IsAccessAllowed(
        CreateFileSystemURL(extension, "system/foo", system_mount_points.get())));
  
    backend.GrantFileAccessToExtension(extension,
                                        base::FilePath(FPL("system/foo")));
    EXPECT_TRUE(backend.IsAccessAllowed(
        CreateFileSystemURL(extension, "system/foo", system_mount_points.get())));
    EXPECT_FALSE(backend.IsAccessAllowed(
        CreateFileSystemURL(extension, "system/foo1",
                            system_mount_points.get())));
  
    // oem is restricted file system.
    backend.GrantFileAccessToExtension(
        extension, base::FilePath(FPL("oem/foo")));
    // The extension should not be able to access the file even if
    // GrantFileAccessToExtension was called.
    EXPECT_FALSE(backend.IsAccessAllowed(
        CreateFileSystemURL(extension, "oem/foo", mount_points.get())));
  
    backend.GrantFullAccessToExtension(extension);
    // The extension should be able to access restricted file system after it was
    // granted full access.
    EXPECT_TRUE(backend.IsAccessAllowed(
        CreateFileSystemURL(extension, "oem/foo", mount_points.get())));
    // The extension which was granted full access should be able to access any
    // path on current file systems.
    EXPECT_TRUE(backend.IsAccessAllowed(
        CreateFileSystemURL(extension, "removable/foo1", mount_points.get())));
    EXPECT_TRUE(backend.IsAccessAllowed(
        CreateFileSystemURL(extension, "system/foo1",
                            system_mount_points.get())));
  
    // The extension cannot access new mount points.
    // TODO(tbarzic): This should probably be changed.
    ASSERT_TRUE(
        mount_points->RegisterFileSystem("test",
                                         storage::kFileSystemTypeNativeLocal,
                                         storage::FileSystemMountOption(),
                                         base::FilePath(FPL("/foo/test"))));
    EXPECT_FALSE(backend.IsAccessAllowed(
        CreateFileSystemURL(extension, "test_/foo", mount_points.get())));
  
    backend.RevokeAccessForExtension(extension);
    EXPECT_FALSE(backend.IsAccessAllowed(
        CreateFileSystemURL(extension, "removable/foo", mount_points.get())));
  }
  
  TEST(ChromeOSFileSystemBackendTest, GetVirtualPathConflictWithSystemPoints) {
    scoped_refptr<content::MockSpecialStoragePolicy> storage_policy =
        new content::MockSpecialStoragePolicy();
    scoped_refptr<storage::ExternalMountPoints> mount_points(
        storage::ExternalMountPoints::CreateRefCounted());
    scoped_refptr<storage::ExternalMountPoints> system_mount_points(
        storage::ExternalMountPoints::CreateRefCounted());
    chromeos::FileSystemBackend backend(NULL,  // drive_delegate
                                        NULL,  // file_system_provider_delegate
                                        NULL,  // mtp_delegate
                                        storage_policy,
                                        mount_points.get(),
                                        system_mount_points.get());
  
    const storage::FileSystemType type = storage::kFileSystemTypeNativeLocal;
    const storage::FileSystemMountOption option =
        storage::FileSystemMountOption();
  
    // Backend specific mount points.
    ASSERT_TRUE(mount_points->RegisterFileSystem(
        "b", type, option, base::FilePath(FPL("/a/b"))));
    ASSERT_TRUE(mount_points->RegisterFileSystem(
        "y", type, option, base::FilePath(FPL("/z/y"))));
    ASSERT_TRUE(mount_points->RegisterFileSystem(
        "n", type, option, base::FilePath(FPL("/m/n"))));
  
    // System mount points
    ASSERT_TRUE(system_mount_points->RegisterFileSystem(
        "gb", type, option, base::FilePath(FPL("/a/b"))));
    ASSERT_TRUE(
        system_mount_points->RegisterFileSystem(
            "gz", type, option, base::FilePath(FPL("/z"))));
    ASSERT_TRUE(system_mount_points->RegisterFileSystem(
         "gp", type, option, base::FilePath(FPL("/m/n/o/p"))));
  
    struct TestCase {
      const base::FilePath::CharType* const local_path;
      bool success;
      const base::FilePath::CharType* const virtual_path;
    };
  
    const TestCase kTestCases[] = {
      // Same paths in both mount points.
      { FPL("/a/b/c/d"), true, FPL("b/c/d") },
      // System mount points path more specific.
      { FPL("/m/n/o/p/r/s"), true, FPL("n/o/p/r/s") },
      // System mount points path less specific.
      { FPL("/z/y/x"), true, FPL("y/x") },
      // Only system mount points path matches.
      { FPL("/z/q/r/s"), true, FPL("gz/q/r/s") },
      // No match.
      { FPL("/foo/xxx"), false, FPL("") },
    };
  
    for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
      // Initialize virtual path with a value.
      base::FilePath virtual_path(FPL("/mount"));
      base::FilePath local_path(kTestCases[i].local_path);
      EXPECT_EQ(kTestCases[i].success,
                backend.GetVirtualPath(local_path, &virtual_path))
          << "Resolving " << kTestCases[i].local_path;
  
      // There are no guarantees for |virtual_path| value if |GetVirtualPath|
      // fails.
      if (!kTestCases[i].success)
        continue;
  
      base::FilePath expected_virtual_path(kTestCases[i].virtual_path);
      EXPECT_EQ(expected_virtual_path, virtual_path)
          << "Resolving " << kTestCases[i].local_path;
    }
  }
  
  }  // namespace
  