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_
|