File: diversion_file_manager.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (160 lines) | stat: -rw-r--r-- 7,244 bytes parent folder | download | duplicates (6)
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
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_FILEAPI_DIVERSION_FILE_MANAGER_H_
#define CHROME_BROWSER_ASH_FILEAPI_DIVERSION_FILE_MANAGER_H_

#include <map>
#include <memory>

#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/functional/callback_forward.h"
#include "storage/browser/file_system/file_stream_reader.h"
#include "storage/browser/file_system/file_stream_writer.h"
#include "storage/browser/file_system/file_system_operation.h"
#include "storage/browser/file_system/file_system_url.h"

namespace ash {

// Manages the creation, destruction and access to Diversion Files.
//
// Chromium's SBFS (//storage/browser/file_system) code implements an
// in-process virtual file system. It presents a traditional, POSIX-like API
// for a block-based file model (e.g. "open; write; write; write; close" or,
// when executing untrusted third-party code where file descriptor Denial of
// Service is a concern, "owc; owc; owc", where "owc" is a combined "open;
// write; close") where writes can be incremental and appending to an existing
// file is assumed to have O(1) complexity, where N is the prior file size.
//
// Some SBFS backends are backed by 'the cloud' instead of by local disk, and
// may be document-based (centered on a one-shot download/upload API) instead
// of block-based (open, read, write and close). In particular, appending M
// bytes to a virtual file (of size N) may first require downloading N bytes
// and then uploading (N + M) bytes, which has O(N) complexity for a single
// write operation. Overall, with a naive implementation, workflows with
// multiple write ops may require a quadratic amount of time and network.
//
// A Diversion File is a local file (with "O(1) append" behavior) that proxies
// or caches these remote files. For read-only workflows, this is basically
// just a local cache. For read-write workflows, writes are diverted to this
// file before ultimately being uploaded to the remote database, and follow-up
// reads (e.g. calculating Safe Browsing hashes of freshly written files) can
// hit the faster local disk instead of the slower remote database.
//
// Diversion (for a specific FileSystemURL) starts with a StartDiverting call
// and stops either explicitly or implicitly. Explicitly means after a
// FinishDiverting call. Implicitly means after an idle_timeout amount of time
// has passed since there were no live (constructed but not yet destroyed)
// FileStreamReader or FileStreamWriter objects for that FileSystemURL (and
// FinishDiverting has still not been called). At most one of explicit_callback
// and implicit_callback will be run.
//
// A DiversionFileManager's methods should only be called from the
// content::BrowserThread::IO thread. Callbacks run on the same thread.
class DiversionFileManager : public base::RefCounted<DiversionFileManager> {
 public:
  enum class StoppedReason {
    kExplicitFinish,
    kImplicitIdle,
  };

  enum class StartDivertingResult {
    kOK,                  // Returned when IsDiverting was false.
    kWasAlreadyDiverted,  // Returned when IsDiverting was true.
  };

  enum class FinishDivertingResult {
    kOK,               // Returned when IsDiverting was true.
    kWasNotDiverting,  // Returned when IsDiverting was false.
  };

  // Presents the cached contents as a ScopedFD to a real (in that it's a
  // kernel-visible file, not an SBFS virtual file), temporary file.
  //
  // If scoped_fd.is_valid() then it will have zero offset (in the "lseek(fd,
  // 0, SEEK_CUR)" sense).
  using Callback = base::OnceCallback<void(StoppedReason stopped_reason,
                                           const storage::FileSystemURL& url,
                                           base::ScopedFD scoped_fd,
                                           int64_t file_size,
                                           base::File::Error error)>;

  DiversionFileManager();

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

  bool IsDiverting(const storage::FileSystemURL& url);

  // Has no effect (and will not return kOK) if IsDiverting(url) was true. "No
  // effect" means that the callback will not be run.
  //
  // The implicit_callback may be null, equivalent to an empty-body Callback (a
  // no-op other than running the base::ScopedFD destructor; the temporary
  // file's contents are discarded).
  StartDivertingResult StartDiverting(const storage::FileSystemURL& url,
                                      base::TimeDelta idle_timeout,
                                      Callback implicit_callback);

  // Has no effect (and will not return kOK) if IsDiverting(url) was false. "No
  // effect" means that the callback will not be run.
  //
  // The explicit_callback may be null, equivalent to an empty-body Callback (a
  // no-op other than running the base::ScopedFD destructor; the temporary
  // file's contents are discarded).
  //
  // If IsDiverting(url) returned true, immediately before FinishDiverting was
  // called, then it will now return false, immediately afterwards. Regardless,
  // the explicit_callback might not run straight away, as it only runs after
  // all of the existing readers and writers are destroyed.
  //
  // If StartDiverting is called (with an equivalent url) after FinishDiverting
  // returns but before the explicit_callback runs, then it starts a new,
  // independent Diversion File. Subsequent reader or writer activity will not
  // affect the file contents or file size seen by explicit_callback.
  FinishDivertingResult FinishDiverting(const storage::FileSystemURL& url,
                                        Callback explicit_callback);

  // Factory methods for objects that allow reading from or writing to
  // Diversion Files. They return nullptr when IsDiverting(url) is false.
  std::unique_ptr<storage::FileStreamReader> CreateDivertedFileStreamReader(
      const storage::FileSystemURL& url,
      int64_t offset);
  std::unique_ptr<storage::FileStreamWriter> CreateDivertedFileStreamWriter(
      const storage::FileSystemURL& url,
      int64_t offset);

  void GetDivertedFileInfo(
      const storage::FileSystemURL& url,
      storage::FileSystemOperation::GetMetadataFieldSet fields,
      base::OnceCallback<void(base::File::Error result,
                              const base::File::Info& file_info)> callback);

  void TruncateDivertedFile(
      const storage::FileSystemURL& url,
      int64_t length,
      base::OnceCallback<void(base::File::Error result)> callback);

  void OverrideTmpfileDirForTesting(const base::FilePath& tmpfile_dir);

 private:
  class Entry;
  class Worker;
  using Map = std::map<storage::FileSystemURL,
                       scoped_refptr<Entry>,
                       storage::FileSystemURL::Comparator>;

  friend class base::RefCounted<DiversionFileManager>;
  ~DiversionFileManager();

  std::string TmpfileDirAsString() const;

  Map entries_;
  base::FilePath tmpfile_dir_;
};

}  // namespace ash

#endif  // CHROME_BROWSER_ASH_FILEAPI_DIVERSION_FILE_MANAGER_H_