| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef STORAGE_BROWSER_FILE_SYSTEM_OBFUSCATED_FILE_UTIL_H_ |
| #define STORAGE_BROWSER_FILE_SYSTEM_OBFUSCATED_FILE_UTIL_H_ |
| |
| #include <stdint.h> |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/component_export.h" |
| #include "base/files/file.h" |
| #include "base/files/file_error_or.h" |
| #include "base/files/file_path.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/sequence_checker.h" |
| #include "base/timer/timer.h" |
| #include "storage/browser/blob/shareable_file_reference.h" |
| #include "storage/browser/file_system/file_system_file_util.h" |
| #include "storage/browser/file_system/file_system_url.h" |
| #include "storage/browser/file_system/obfuscated_file_util_delegate.h" |
| #include "storage/browser/file_system/sandbox_directory_database.h" |
| #include "storage/browser/file_system/sandbox_file_system_backend_delegate.h" |
| #include "storage/common/file_system/file_system_types.h" |
| |
| namespace blink { |
| class StorageKey; |
| } // namespace blink |
| |
| namespace storage { |
| |
| class FileSystemOperationContext; |
| class ObfuscatedFileUtilTest; |
| class QuotaBackendImplTest; |
| class SandboxOriginDatabaseInterface; |
| class SpecialStoragePolicy; |
| |
| // Class representing the key for directories_. NOTE: The `bucket` value is |
| // optional due to usage of ObfuscatedFileUtil where the type is not kTemporary |
| // (i.e. kPersistent or kSyncable). For all non-temporary types, expect the |
| // bucket member value to be absl::nullopt. The class is implemented as such to |
| // avoid mapping the same StorageKey to potentially different bucket values, |
| // which would cause directories_ lookup errors. NOTE: The `type_string` value |
| // is empty when designating a "top-level directory" or a directory that |
| // contains one or more subdirectories with a non-empty type. This class stores |
| // a string rather than the FileSystemType itself because multiple |
| // FileSystemTypes can map to the same `type_string`, and preserving this |
| // behavior is necessary to retrieving and deleting ObfuscatedFilePaths |
| // correctly. |
| class DatabaseKey { |
| public: |
| DatabaseKey(); |
| ~DatabaseKey(); |
| |
| // Copyable and movable |
| DatabaseKey(const DatabaseKey& other); |
| DatabaseKey& operator=(const DatabaseKey& other); |
| DatabaseKey(DatabaseKey&& other); |
| DatabaseKey& operator=(DatabaseKey&& other); |
| |
| DatabaseKey(const blink::StorageKey& storage_key, |
| const absl::optional<BucketLocator>& bucket, |
| const std::string& type_string); |
| |
| const blink::StorageKey& storage_key() const { return storage_key_; } |
| const absl::optional<BucketLocator>& bucket() const { return bucket_; } |
| const std::string& type() const { return type_; } |
| |
| bool operator==(const DatabaseKey& other) const; |
| bool operator!=(const DatabaseKey& other) const; |
| bool operator<(const DatabaseKey& other) const; |
| |
| private: |
| blink::StorageKey storage_key_; |
| absl::optional<BucketLocator> bucket_; |
| std::string type_; |
| }; |
| |
| // This file util stores directory information in either LevelDB or |
| // StorageBuckets to obfuscate and to neutralize virtual file paths given by |
| // arbitrary apps. Files are stored with three-level isolation: (1) |
| // per-StorageKey, (2) per-bucket, and (3) per-type. The isolation is done by |
| // storing data in separate directory partitions. For example, a file in |
| // Temporary file system for origin 'www.example.com' is stored in a different |
| // partition from a file in Persistent file system for the same origin, or from |
| // a file in a Temporary file system for another origin. Similarly, a file in a |
| // Temporary file system for origin 'www.foo.com' with a default bucket is |
| // stored in a different partition from a non-default bucket for the same origin |
| // and Temporary file system. |
| // |
| // * For default first-party StorageKeys, per-origin directory name information |
| // is stored in a separate LevelDB, which is maintained by |
| // SandboxOriginDatabase. For per-type information, we use a small static |
| // mapping (e.g. 't' for Temporary type) for regular sandbox filesystems. |
| // NOTE/TODO(https://crbug.com/1349156): the goal is to eventually deprecate |
| // SandboxOriginDatabase and rely entirely on Storage Buckets. |
| // * For all other StorageKeys, we rely on quota management of Storage Buckets |
| // in addition to the same static mapping of per-type information described |
| // above. |
| // |
| // The overall implementation philosophy of this class is that partial failures |
| // should leave us with an intact database; we'd prefer to leak the occasional |
| // backing file than have a database entry whose backing file is missing. When |
| // doing FSCK operations, if you find a loose backing file with no reference, |
| // you may safely delete it. |
| // |
| // This class must be deleted on the FILE thread, because that's where |
| // DropDatabases needs to be called. |
| class COMPONENT_EXPORT(STORAGE_BROWSER) ObfuscatedFileUtil |
| : public FileSystemFileUtil { |
| public: |
| // StorageKey enumerator interface. |
| // An instance of this interface is assumed to be called on the file thread. |
| // NOTE: currently, ObfuscatedFileUtil still relies on SandboxOriginDatabases |
| // for first-party StorageKeys/default buckets. The |
| // AbstractStorageKeyEnumerator is only used in these cases. While this class |
| // stores StorageKeys, it ultimately relies on only the origin information to |
| // access the appropriate SandboxOriginDatabase. |
| class AbstractStorageKeyEnumerator { |
| public: |
| virtual ~AbstractStorageKeyEnumerator() = default; |
| |
| // Returns the next StorageKey. Returns absl::nullopt if there are no more |
| // StorageKeys. |
| virtual absl::optional<blink::StorageKey> Next() = 0; |
| |
| // Returns the current StorageKey's information. |
| // `type_string` must be ascii string. |
| virtual bool HasTypeDirectory(const std::string& type_string) const = 0; |
| }; |
| |
| // The FileSystem directory component. |
| static const base::FilePath::CharType kFileSystemDirectory[]; |
| |
| // The type string is used to provide per-type isolation in the sandboxed |
| // filesystem directory. `known_type_strings` are known type string names that |
| // this file system should care about. This info is used to determine whether |
| // we could delete the entire origin directory or not in |
| // DeleteDirectoryForBucketAndType. If no directory for any known type |
| // exists the origin directory may get deleted when one StorageKey/type pair |
| // is deleted. NOTE: type strings are not mapped 1-to-1 with FileSystemType, |
| // and as a result, directories should only be directly compared using type |
| // string values. |
| ObfuscatedFileUtil(scoped_refptr<SpecialStoragePolicy> special_storage_policy, |
| const base::FilePath& profile_path, |
| leveldb::Env* env_override, |
| const std::set<std::string>& known_type_strings, |
| SandboxFileSystemBackendDelegate* sandbox_delegate, |
| bool is_incognito); |
| |
| ObfuscatedFileUtil(const ObfuscatedFileUtil&) = delete; |
| ObfuscatedFileUtil& operator=(const ObfuscatedFileUtil&) = delete; |
| |
| ~ObfuscatedFileUtil() override; |
| |
| // FileSystemFileUtil overrides. |
| base::File CreateOrOpen(FileSystemOperationContext* context, |
| const FileSystemURL& url, |
| int file_flags) override; |
| base::File::Error EnsureFileExists(FileSystemOperationContext* context, |
| const FileSystemURL& url, |
| bool* created) override; |
| base::File::Error CreateDirectory(FileSystemOperationContext* context, |
| const FileSystemURL& url, |
| bool exclusive, |
| bool recursive) override; |
| base::File::Error GetFileInfo(FileSystemOperationContext* context, |
| const FileSystemURL& url, |
| base::File::Info* file_info, |
| base::FilePath* platform_file) override; |
| std::unique_ptr<AbstractFileEnumerator> CreateFileEnumerator( |
| FileSystemOperationContext* context, |
| const FileSystemURL& root_url, |
| bool recursive) override; |
| base::File::Error GetLocalFilePath(FileSystemOperationContext* context, |
| const FileSystemURL& file_system_url, |
| base::FilePath* local_path) override; |
| base::File::Error Touch(FileSystemOperationContext* context, |
| const FileSystemURL& url, |
| const base::Time& last_access_time, |
| const base::Time& last_modified_time) override; |
| base::File::Error Truncate(FileSystemOperationContext* context, |
| const FileSystemURL& url, |
| int64_t length) override; |
| base::File::Error CopyOrMoveFile(FileSystemOperationContext* context, |
| const FileSystemURL& src_url, |
| const FileSystemURL& dest_url, |
| CopyOrMoveOptionSet options, |
| bool copy) override; |
| base::File::Error CopyInForeignFile(FileSystemOperationContext* context, |
| const base::FilePath& src_file_path, |
| const FileSystemURL& dest_url) override; |
| base::File::Error DeleteFile(FileSystemOperationContext* context, |
| const FileSystemURL& url) override; |
| base::File::Error DeleteDirectory(FileSystemOperationContext* context, |
| const FileSystemURL& url) override; |
| ScopedFile CreateSnapshotFile(FileSystemOperationContext* context, |
| const FileSystemURL& url, |
| base::File::Error* error, |
| base::File::Info* file_info, |
| base::FilePath* platform_path) override; |
| |
| // Returns true if the directory `url` is empty. |
| bool IsDirectoryEmpty(FileSystemOperationContext* context, |
| const FileSystemURL& url); |
| |
| // Gets the topmost directory specific to this BucketLocator and type. This |
| // will contain both the directory database's files and all the backing file |
| // subdirectories. |
| // Returns the topmost origin directory if `type` is empty. |
| // Returns a base::FileError if the directory is undefined. |
| base::FileErrorOr<base::FilePath> GetDirectoryForBucketAndType( |
| const BucketLocator& bucket_locator, |
| const absl::optional<FileSystemType>& type, |
| bool create); |
| |
| // Gets the topmost directory specific to this StorageKey and type. This will |
| // contain both the directory database's files and all the backing file |
| // subdirectories. |
| // Returns the topmost origin directory if `type` is empty. |
| // Returns an empty path if the directory is undefined. |
| // If the directory is defined, it will be returned, even if |
| // there is a file system error (e.g. the directory doesn't exist on disk and |
| // `create` is false). |
| base::FileErrorOr<base::FilePath> GetDirectoryForStorageKeyAndType( |
| const blink::StorageKey& storage_key, |
| const absl::optional<FileSystemType>& type, |
| bool create); |
| |
| // Deletes the topmost directory specific to this BucketLocator and type. This |
| // will delete its directory database. Deletes the topmost bucket |
| // directory if `type` is absl::nullopt. |
| bool DeleteDirectoryForBucketAndType( |
| const BucketLocator& bucket_locator, |
| const absl::optional<FileSystemType>& type); |
| |
| // This method and all methods of its returned class must be called only on |
| // the FILE thread. The caller is responsible for deleting the returned |
| // object. |
| std::unique_ptr<AbstractStorageKeyEnumerator> CreateStorageKeyEnumerator(); |
| |
| // Deletes a directory database from the database list and destroys the |
| // database on the disk corresponding to the provided bucket locator and type. |
| void DestroyDirectoryDatabaseForBucket( |
| const BucketLocator& bucket_locator, |
| const absl::optional<FileSystemType>& type); |
| |
| // Computes a cost for storing a given file in the obfuscated FSFU. |
| // As the cost of a file is independent of the cost of its parent directories, |
| // this ignores all but the BaseName of the supplied path. In order to |
| // compute the cost of adding a multi-segment directory recursively, call this |
| // on each path segment and add the results. |
| static int64_t ComputeFilePathCost(const base::FilePath& path); |
| |
| // This will rewrite the databases to remove traces of deleted data from disk. |
| void RewriteDatabases(); |
| |
| // This function removes the key-value pair from default_buckets_ keyed at |
| // `storage_key`. Called when a default bucket is deleted from Quota |
| // management and the default_buckets_ cache needs to be updated to reflect |
| // that change in state. |
| void DeleteDefaultBucketForStorageKey(const blink::StorageKey& storage_key); |
| |
| bool is_incognito() { return is_incognito_; } |
| |
| ObfuscatedFileUtilDelegate* delegate() { return delegate_.get(); } |
| // Not owned. |
| SandboxFileSystemBackendDelegate* sandbox_delegate() { |
| return sandbox_delegate_; |
| } |
| |
| private: |
| using FileId = SandboxDirectoryDatabase::FileId; |
| using FileInfo = SandboxDirectoryDatabase::FileInfo; |
| |
| friend class ObfuscatedFileEnumerator; |
| friend class ObfuscatedFileUtilTest; |
| friend class QuotaBackendImplTest; |
| friend class SandboxFileSystemBackendDelegate; |
| |
| // Helper method to create an obfuscated file util for regular |
| // (temporary, persistent) file systems. Used only for testing. |
| // Note: this is implemented in sandbox_file_system_backend_delegate.cc. |
| static std::unique_ptr<ObfuscatedFileUtil> CreateForTesting( |
| scoped_refptr<SpecialStoragePolicy> special_storage_policy, |
| const base::FilePath& file_system_directory, |
| leveldb::Env* env_override, |
| bool is_incognito); |
| |
| base::FileErrorOr<base::FilePath> GetDirectoryForURL(const FileSystemURL& url, |
| bool create); |
| |
| base::File::Error GetFileInfoInternal(SandboxDirectoryDatabase* db, |
| FileSystemOperationContext* context, |
| const FileSystemURL& url, |
| FileId file_id, |
| FileInfo* local_info, |
| base::File::Info* file_info, |
| base::FilePath* platform_file_path); |
| |
| // Creates a new file, both the underlying backing file and the entry in the |
| // database. `dest_file_info` is an in-out parameter. Supply the name and |
| // parent_id; data_path is ignored. On success, data_path will |
| // always be set to the relative path [from the root of the type-specific |
| // filesystem directory] of a NEW backing file. Returns the new file. |
| base::File CreateAndOpenFile(FileSystemOperationContext* context, |
| const FileSystemURL& dest_url, |
| FileInfo* dest_file_info, |
| int file_flags); |
| |
| // The same as CreateAndOpenFile except that a file is not returned and if a |
| // path is provided in `source_path`, it will be used as a source from which |
| // to COPY data. If `foreign_source` is true, the source file is considered |
| // from another (on disk) file system and its path is considered not |
| // obfuscated. |
| base::File::Error CreateFile(FileSystemOperationContext* context, |
| const base::FilePath& source_file_path, |
| bool foreign_source, |
| const FileSystemURL& dest_url, |
| FileInfo* dest_file_info); |
| |
| // Updates `db` and `dest_file_info` at the end of creating a new file. |
| base::File::Error CommitCreateFile(const base::FilePath& root, |
| const base::FilePath& local_path, |
| SandboxDirectoryDatabase* db, |
| FileInfo* dest_file_info); |
| |
| // This converts from a relative path [as is stored in the FileInfo.data_path |
| // field] to an absolute platform path that can be given to the native |
| // filesystem. |
| base::FilePath DataPathToLocalPath(const FileSystemURL& url, |
| const base::FilePath& data_file_path); |
| |
| // This returns nullptr if `create` flag is false and a filesystem does not |
| // exist for the given `url`. |
| // For read operations `create` should be false. |
| SandboxDirectoryDatabase* GetDirectoryDatabase(const FileSystemURL& url, |
| bool create); |
| |
| // Gets the topmost directory specific to this StorageKey. This will |
| // contain both of the filesystem type subdirectories. |
| // NOTE: this function uses base::ScopedAllowBaseSyncPrimitives and |
| // calls QuotaManagerProxy::GetOrCreateBucketSync() which relies on a |
| // blocking base::WaitableEvent. |
| base::FileErrorOr<base::FilePath> GetDirectoryForStorageKey( |
| const blink::StorageKey& storage_key, |
| bool create); |
| |
| // A helper function used by the GetDirectoryFor* methods to ensure that |
| // `path` is a valid directory or that a valid directory can be constructed. |
| base::File::Error GetDirectoryHelper(const base::FilePath& path, bool create); |
| |
| void InvalidateUsageCache(FileSystemOperationContext* context, |
| const blink::StorageKey& storage_key, |
| FileSystemType type); |
| |
| // Given a StorageKey, retrieve its default bucket either from the |
| // default_buckets_ in-memory structure or via GetOrCreateBucketSync(). NOTE: |
| // this function may use base::ScopedAllowBaseSyncPrimitives and call |
| // QuotaManagerProxy::GetOrCreateBucketSync() which relies on a blocking |
| // base::WaitableEvent. |
| QuotaErrorOr<BucketLocator> GetOrCreateDefaultBucket( |
| const blink::StorageKey& storage_key); |
| |
| void MarkUsed(); |
| void DropDatabases(); |
| |
| // Initializes the origin/type database. `origin_hint` may be used as a |
| // hint for initializing database if it's not empty. |
| bool InitOriginDatabase(const url::Origin& origin_hint, bool create); |
| |
| base::File::Error GenerateNewLocalPath(SandboxDirectoryDatabase* db, |
| FileSystemOperationContext* context, |
| const FileSystemURL& url, |
| base::FilePath* root, |
| base::FilePath* local_path); |
| |
| base::File CreateOrOpenInternal(FileSystemOperationContext* context, |
| const FileSystemURL& url, |
| int file_flags); |
| |
| bool HasIsolatedStorage(const blink::StorageKey& storage_key); |
| |
| SEQUENCE_CHECKER(sequence_checker_); |
| |
| // Keeps tracks of previously-seen default buckets mapped to their |
| // corresponding StorageKey. Should remain in parallel with directories_. |
| std::map<blink::StorageKey, BucketLocator> default_buckets_; |
| std::map<DatabaseKey, std::unique_ptr<SandboxDirectoryDatabase>> directories_; |
| std::unique_ptr<SandboxOriginDatabaseInterface> origin_database_; |
| scoped_refptr<SpecialStoragePolicy> special_storage_policy_; |
| base::FilePath file_system_directory_; |
| raw_ptr<leveldb::Env> env_override_; |
| bool is_incognito_; |
| |
| // Used to delete database after a certain period of inactivity. |
| int64_t db_flush_delay_seconds_; |
| |
| base::OneShotTimer timer_; |
| |
| std::set<std::string> known_type_strings_; |
| |
| // Not owned. |
| raw_ptr<SandboxFileSystemBackendDelegate> sandbox_delegate_; |
| |
| std::unique_ptr<ObfuscatedFileUtilDelegate> delegate_; |
| }; |
| |
| } // namespace storage |
| |
| #endif // STORAGE_BROWSER_FILE_SYSTEM_OBFUSCATED_FILE_UTIL_H_ |