1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188

content / browser / file_system_access / file_system_access_lock_manager.h [blame]

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_LOCK_MANAGER_H_
#define CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_LOCK_MANAGER_H_

#include <map>
#include <optional>

#include "base/files/file_path.h"
#include "base/memory/ref_counted_delete_on_sequence.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/types/id_type.h"
#include "base/types/pass_key.h"
#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
#include "content/common/content_export.h"
#include "content/public/browser/global_routing_id.h"

namespace storage {
class FileSystemURL;
}  // namespace storage

namespace content {

class FileSystemAccessManagerImpl;
class Lock;
class RootLock;

// This class is in charge of the creation of Locks. Locks restrict the access
// to a specific file or directory, preventing unexpected concurrent access to
// data. It is owned by the FileSystemAccessManagerImpl.
class CONTENT_EXPORT FileSystemAccessLockManager
    : public base::RefCountedDeleteOnSequence<FileSystemAccessLockManager> {
 public:
  class LockHandle;

  using PassKey = base::PassKey<FileSystemAccessLockManager>;

  // This type represents a locking type used to prevent other locking types
  // from acquiring a lock.
  using LockType = base::IdType32<class LockTypeTag>;

  using TakeLockCallback = base::OnceCallback<void(scoped_refptr<LockHandle>)>;

  // A handle to a `Lock` passed to the frame that holds the lock. The `Lock` is
  // kept alive as long as there is some `LockHandle` to it.
  class CONTENT_EXPORT LockHandle : public base::RefCounted<LockHandle> {
   public:
    LockHandle(LockHandle const&) = delete;
    LockHandle& operator=(LockHandle const&) = delete;

    const LockType& type() const { return type_; }

    bool IsExclusive() const { return is_exclusive_; }

   private:
    friend class Lock;
    friend class base::RefCounted<LockHandle>;

    LockHandle(base::WeakPtr<Lock> lock,
               scoped_refptr<LockHandle> parent_lock_handle,
               const GlobalRenderFrameHostId& frame_id);

    // On destruction, lets its `lock_` know it is no longer held.
    ~LockHandle();

    SEQUENCE_CHECKER(sequence_checker_);

    base::WeakPtr<Lock> lock_ GUARDED_BY_CONTEXT(sequence_checker_);
    const LockType type_;
    const bool is_exclusive_;

    const scoped_refptr<LockHandle> parent_lock_handle_;

    // The frame id of the associated render frame host that holds this lock
    // handle.
    const GlobalRenderFrameHostId frame_id_;
  };

  explicit FileSystemAccessLockManager(
      base::PassKey<FileSystemAccessManagerImpl> pass_key);

  FileSystemAccessLockManager(FileSystemAccessLockManager const&) = delete;
  FileSystemAccessLockManager& operator=(FileSystemAccessLockManager const&) =
      delete;

  // Attempts to take a lock of `lock_type` on `url`. Passes a handle of the
  // lock to `callback` if successful. The lock is released when there are no
  // handles to it.
  //
  // `frame_id` is the `GlobalRenderFrameHostId` of the render frame host
  // associated with the frame that holds this handle.
  //
  // If there is an existing lock that is in contention with the `lock_type`, it
  // will evict pages that hold the existing lock if they are all inactive (e.g.
  // in the BFCache).
  void TakeLock(const GlobalRenderFrameHostId& frame_id,
                const storage::FileSystemURL& url,
                LockType lock_type,
                TakeLockCallback callback);

  // Returns true if there is not an existing lock on `url` that is contentious
  // with `lock_type`.
  //
  // This may return `false` but the same arguments would succeed for `TakeLock`
  // since `TakeLock` may evict pages to take the lock.
  bool IsContentious(const storage::FileSystemURL& url, LockType lock_type);

  // Creates a new shared lock type.
  [[nodiscard]] LockType CreateSharedLockType();

  // Gets the exclusive lock type.
  [[nodiscard]] LockType GetExclusiveLockType();

  // Gets the `ancestor_lock_type_` for testing.
  [[nodiscard]] LockType GetAncestorLockTypeForTesting();

  // Gets a weak pointer to `this` to test if its been destroyed.
  [[nodiscard]] base::WeakPtr<FileSystemAccessLockManager>
  GetWeakPtrForTesting();

 private:
  friend class base::DeleteHelper<FileSystemAccessLockManager>;
  friend class base::RefCountedDeleteOnSequence<FileSystemAccessLockManager>;

  friend RootLock;

  ~FileSystemAccessLockManager();

  enum class EntryPathType {
    // A path on the local file system. Files with these paths can be operated
    // on by base::File.
    kLocal,

    // A path on an "external" file system. These paths can only be accessed via
    // the filesystem abstraction in //storage/browser/file_system, and a
    // storage::FileSystemURL of type storage::kFileSystemTypeExternal.
    kExternal,

    // A path from a sandboxed file system. These paths can be accessed by a
    // storage::FileSystemURL of type storage::kFileSystemTypeTemporary.
    kSandboxed,
  };

  struct RootLocator {
    static RootLocator FromFileSystemURL(const storage::FileSystemURL& url);

    RootLocator(const EntryPathType& type,
                const std::optional<storage::BucketLocator>& bucket_locator);
    RootLocator(const RootLocator&);
    ~RootLocator();

    bool operator<(const RootLocator& other) const;

    const EntryPathType type;
    // Non-null iff `type` is kSandboxed.
    const std::optional<storage::BucketLocator> bucket_locator;
  };

  // Releases the root lock for `root_locator`. Called from the RootLock.
  void ReleaseRoot(const RootLocator& root_locator);

  RootLock* GetRootLock(const RootLocator& root_locator);
  RootLock* GetOrCreateRootLock(const RootLocator& root_locator);

  SEQUENCE_CHECKER(sequence_checker_);

  std::map<RootLocator, std::unique_ptr<RootLock>> root_locks_
      GUARDED_BY_CONTEXT(sequence_checker_);

  LockType::Generator lock_type_generator_;

  LockType exclusive_lock_type_ = lock_type_generator_.GenerateNextId();

  // The shared lock type that the lock manager uses to lock ancestors of locked
  // entry locators. Should not be used outside of the lock manager or testing.
  LockType ancestor_lock_type_ = lock_type_generator_.GenerateNextId();

  base::WeakPtrFactory<FileSystemAccessLockManager> weak_factory_
      GUARDED_BY_CONTEXT(sequence_checker_){this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_LOCK_MANAGER_H_