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 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
|
// Copyright 2022 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_FUSEBOX_FUSEBOX_READ_WRITER_H_
#define CHROME_BROWSER_ASH_FUSEBOX_FUSEBOX_READ_WRITER_H_
#include <utility>
#include "base/files/scoped_file.h"
#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "base/types/expected.h"
#include "chrome/browser/ash/fusebox/fusebox.pb.h"
#include "net/base/io_buffer.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_context.h"
namespace fusebox {
// Caches a storage::FileStreamReader (and FileStreamWriter) and their offsets
// so that, when consecutive reads (or consecutive writes) are adjacent (the
// second one starts where the first one ends), the FileStreamReader (or
// FileStreamWriter) is re-used.
//
// When serving a "stateless" I/O API that passes an offset each time (such as
// the FUSE API), but the underlying storage::AsyncFileUtil doesn't support
// seeking, then re-using a cached FileStreamReader can often avoid "Shlemiel
// the Painter" quadratic performance. See
// https://wiki.c2.com/?ShlemielThePainter
//
// Each ReadWriter instance lives entirely on the I/O thread, but its owner
// (and the callbacks) must live on the UI thread (and wrap the ReadWriter in a
// base::SequenceBound).
//
// The owner is also responsible for ensuring that only one operation is in
// flight at any one time. "An operation" starts with a Read or Write call and
// ends just before the corresponding callback is run.
class ReadWriter {
public:
using Close2Callback =
base::OnceCallback<void(const Close2ResponseProto& response)>;
using FlushCallback =
base::OnceCallback<void(const FlushResponseProto& response)>;
using Read2Callback =
base::OnceCallback<void(const Read2ResponseProto& response)>;
using Write2Callback =
base::OnceCallback<void(const Write2ResponseProto& response)>;
// |use_temp_file| is for the case when the |fs_url| storage system does not
// support incremental writes, only atomic "here's the entire contents"
// writes, such as MTP (Media Transfer Protocol, e.g. phones attached to a
// Chromebook, which is a file-oriented rather than block-oriented protocol).
// In this case, the Write calls are diverted to a temporary file and the
// Close call saves that temporary file to |fs_url|.
//
// |temp_file_starts_with_copy| states whether to initialize that temporary
// file with a copy of the underlying file. If |temp_file_starts_with_copy|
// false, the temporary file is initially empty. When |use_temp_file| is
// false, |temp_file_starts_with_copy| is ignored.
ReadWriter(const storage::FileSystemURL& fs_url,
const std::string& profile_path,
bool use_temp_file,
bool temp_file_starts_with_copy);
~ReadWriter();
void Close(scoped_refptr<storage::FileSystemContext> fs_context,
Close2Callback callback);
void Flush(scoped_refptr<storage::FileSystemContext> fs_context,
FlushCallback callback);
void Read(scoped_refptr<storage::FileSystemContext> fs_context,
int64_t offset,
int64_t length,
Read2Callback callback);
void Write(scoped_refptr<storage::FileSystemContext> fs_context,
scoped_refptr<net::StringIOBuffer> buffer,
int64_t offset,
int length,
Write2Callback callback);
// The int is a POSIX error code.
using WriteTempFileResult = std::pair<base::ScopedFD, int>;
private:
// Saves the |temp_file_| to the |fs_url_|.
void Save();
// The CallXxx and OnXxx methods are static (but take a WeakPtr) so that the
// callback will run even if the WeakPtr is invalidated.
static void OnDefaultFlush(
base::WeakPtr<ReadWriter> weak_ptr,
FlushCallback callback,
scoped_refptr<storage::FileSystemContext> fs_context,
int flush_posix_error_code);
static void OnEOFFlushBeforeActualClose(
base::WeakPtr<ReadWriter> weak_ptr,
Close2Callback callback,
scoped_refptr<storage::FileSystemContext> fs_context,
std::unique_ptr<storage::FileStreamWriter> fs_writer,
int flush_posix_error_code);
static void OnTempFileInitialized(base::WeakPtr<ReadWriter> weak_ptr,
scoped_refptr<net::StringIOBuffer> buffer,
int64_t offset,
int length,
Write2Callback callback,
base::expected<base::ScopedFD, int> result);
static void CallWriteTempFile(base::WeakPtr<ReadWriter> weak_ptr,
scoped_refptr<net::StringIOBuffer> buffer,
int64_t offset,
int length,
Write2Callback callback);
static void OnRead(base::WeakPtr<ReadWriter> weak_ptr,
Read2Callback callback,
scoped_refptr<storage::FileSystemContext> fs_context,
std::unique_ptr<storage::FileStreamReader> fs_reader,
scoped_refptr<net::IOBuffer> buffer,
int64_t offset,
int length);
static void OnWriteTempFile(base::WeakPtr<ReadWriter> weak_ptr,
Write2Callback callback,
WriteTempFileResult result);
static void OnEOFFlushBeforeCallWriteDirect(
base::WeakPtr<ReadWriter> weak_ptr,
Write2Callback callback,
scoped_refptr<storage::FileSystemContext> fs_context,
scoped_refptr<net::IOBuffer> buffer,
int64_t offset,
int length,
std::unique_ptr<storage::FileStreamWriter> fs_writer,
int flush_posix_error_code);
void CallWriteDirect(Write2Callback callback,
scoped_refptr<storage::FileSystemContext> fs_context,
std::unique_ptr<storage::FileStreamWriter> fs_writer,
scoped_refptr<net::IOBuffer> buffer,
int64_t offset,
int length);
static void OnWriteDirect(
base::WeakPtr<ReadWriter> weak_ptr,
Write2Callback callback,
scoped_refptr<storage::FileSystemContext> fs_context,
std::unique_ptr<storage::FileStreamWriter> fs_writer,
scoped_refptr<net::IOBuffer> buffer,
int64_t offset,
int length);
const storage::FileSystemURL fs_url_;
const std::string profile_path_;
std::unique_ptr<storage::FileStreamReader> fs_reader_;
// Unused (and set to -1) whenever fs_reader_ is nullptr. When std::move'ing
// (or otherwise changing) the fs_reader_, we therefore assign (via = or
// std::exchange) to read_offset_ at the same time.
int64_t read_offset_ = -1;
std::unique_ptr<storage::FileStreamWriter> fs_writer_;
// Unused (and set to -1) whenever fs_writer_ is nullptr. When std::move'ing
// (or otherwise changing) the fs_writer_, we therefore assign (via = or
// std::exchange) to write_offset_ at the same time.
int64_t write_offset_ = -1;
scoped_refptr<storage::FileSystemContext> close2_fs_context_;
Close2Callback close2_callback_;
base::ScopedFD temp_file_;
// The first (if any) write error we encounter. When non-zero, all future
// Write calls fail and Save-on-Close is a no-op (other than running the
// Close2Callback).
int write_posix_error_code_ = 0;
// True when the FD in |temp_file_| has been loaned out to a separate thread
// (separate from the content::BrowserThread::IO thread that this lives on,
// which should not be used for blocking I/O).
bool is_loaning_temp_file_scoped_fd_ = false;
bool is_in_flight_ = false;
bool closed_ = false;
bool created_temp_file_ = false;
// storage::FileStreamWriter::Flush takes a storage::FlushMode parameter.
// This bool field is about calling with FlushMode::kEndOfFile, not with
// FlushMode::kDefault.
bool fs_writer_needs_eof_flushing_ = false;
const bool use_temp_file_;
const bool temp_file_starts_with_copy_;
base::WeakPtrFactory<ReadWriter> weak_ptr_factory_{this};
};
} // namespace fusebox
#endif // CHROME_BROWSER_ASH_FUSEBOX_FUSEBOX_READ_WRITER_H_
|