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
|
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/file_system_access/file_system_access_local_path_watcher.h"
#include "base/files/file_path.h"
#include "base/task/bind_post_task.h"
#include "base/task/thread_pool.h"
#include "content/browser/file_system_access/file_path_watcher/file_path_watcher.h"
#include "content/browser/file_system_access/file_system_access_error.h"
#include "content/browser/file_system_access/file_system_access_watcher_manager.h"
#include "storage/browser/file_system/file_system_url.h"
#include "third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom-shared.h"
namespace content {
namespace {
// Creates a task runner suitable for file path watching.
scoped_refptr<base::SequencedTaskRunner> CreateFilePathWatcherTaskRunner() {
return base::ThreadPool::CreateSequencedTaskRunner({
// Needed for file I/O.
base::MayBlock(),
// File path watching is likely not user visible.
base::TaskPriority::BEST_EFFORT,
// File path watching should not block shutdown.
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN,
});
}
} // namespace
FileSystemAccessLocalPathWatcher::FileSystemAccessLocalPathWatcher(
FileSystemAccessWatchScope scope,
scoped_refptr<storage::FileSystemContext> file_system_context,
base::PassKey<FileSystemAccessWatcherManager> /*pass_key*/)
: FileSystemAccessChangeSource(std::move(scope),
std::move(file_system_context)),
watcher_(CreateFilePathWatcherTaskRunner()) {}
FileSystemAccessLocalPathWatcher::~FileSystemAccessLocalPathWatcher() = default;
void FileSystemAccessLocalPathWatcher::Initialize(
base::OnceCallback<void(blink::mojom::FileSystemAccessErrorPtr)>
on_source_initialized) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (scope().IsRecursive() && !FilePathWatcher::RecursiveWatchAvailable()) {
std::move(on_source_initialized)
.Run(file_system_access_error::FromStatus(
blink::mojom::FileSystemAccessStatus::kNotSupportedError));
return;
}
FilePathWatcher::CallbackWithChangeInfo on_change_callback =
base::BindRepeating(&FileSystemAccessLocalPathWatcher::OnFilePathChanged,
weak_factory_.GetWeakPtr());
FilePathWatcher::UsageChangeCallback on_usage_change_callback =
base::BindRepeating(&FileSystemAccessLocalPathWatcher::OnUsageChange,
weak_factory_.GetWeakPtr());
FilePathWatcher::WatchOptions watch_options{
.type = scope().IsRecursive() ? FilePathWatcher::Type::kRecursive
: FilePathWatcher::Type::kNonRecursive,
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN) || \
BUILDFLAG(IS_MAC)
// Note: `report_modified_path` is also present on Android
// and Fuchsia. Update this switch if support for watching
// the local file system is added on those platforms.
//
// TODO(crbug.com/40260973): Report the affected
// path on more platforms.
.report_modified_path = true,
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN) ||
// BUILDFLAG(IS_MAC)
};
watch_path_ = scope().root_url().path();
// For file observations, we watch the parent directory so we can report file
// deletes when the parent directory is deleted. This also helps prevent the
// crswap move to its target file showing up as an appeared event.
// Additionally, the watcher manager needs to know this info to be able to
// convert that event to a modify.
if (scope().GetWatchType() == FileSystemAccessWatchScope::WatchType::kFile) {
watch_path_ = watch_path_.DirName();
}
watcher_.AsyncCall(&FilePathWatcher::WatchWithChangeInfo)
.WithArgs(
watch_path_, std::move(watch_options),
base::BindPostTaskToCurrentDefault(std::move(on_change_callback)),
base::BindPostTaskToCurrentDefault(
std::move(on_usage_change_callback)))
.Then(base::BindOnce(
[](base::WeakPtr<FileSystemAccessLocalPathWatcher> self,
base::OnceCallback<void(blink::mojom::FileSystemAccessErrorPtr)>
callback,
std::optional<size_t> current_usage) {
if (!current_usage.has_value()) {
std::move(callback).Run(file_system_access_error::FromStatus(
blink::mojom::FileSystemAccessStatus::kOperationFailed));
return;
}
self->current_usage_ = current_usage.value();
std::move(callback).Run(file_system_access_error::Ok());
},
weak_factory_.GetWeakPtr(), std::move(on_source_initialized)));
}
size_t FileSystemAccessLocalPathWatcher::current_usage() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return current_usage_;
}
void FileSystemAccessLocalPathWatcher::OnFilePathChanged(
const FilePathWatcher::ChangeInfo& change_info,
const base::FilePath& changed_path,
bool error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Notify if any error occurs without further processing.
if (error) {
NotifyOfChange(base::FilePath(), error, change_info);
return;
}
const base::FilePath& root_path = scope().root_url().path();
// File observations result in the parent directory being watched.
if (scope().GetWatchType() == FileSystemAccessWatchScope::WatchType::kFile &&
changed_path != root_path) {
// This logic is a backup for when we don't get a delete event for the
// `root_path`. When the parent is deleted, so is the file. We updated the
// `change_info.modified_path` to the file's path and the
// `change_info.file_path_type` to `kFile`. After reporting the file
// deletion, we report an error because the parent directory has been
// deleted. Eg. On macOS, we only receive a single deleted event, at the
// parent directory level.
if (changed_path == watch_path_ &&
change_info.change_type == FilePathWatcher::ChangeType::kDeleted) {
FilePathWatcher::ChangeInfo file_change_info = {
FilePathWatcher::FilePathType::kFile,
FilePathWatcher::ChangeType::kDeleted, root_path};
NotifyOfChange(base::FilePath(), error, file_change_info);
} else if (change_info.moved_from_path == root_path) {
// If the file is renamed, we report a kDeleted event for the `root_path`.
// If we were directly watching the file, this would be handled in the
// FilePathWatcher which would convert the event to a kDeleted since
// renaming a watched file is a move out of scope.
FilePathWatcher::ChangeInfo file_change_info = {
FilePathWatcher::FilePathType::kFile,
FilePathWatcher::ChangeType::kDeleted, root_path};
NotifyOfChange(base::FilePath(), error, file_change_info);
}
return;
}
base::FilePath relative_path;
if (root_path.empty()) {
relative_path = changed_path;
} else if (root_path.IsParent(changed_path)) {
CHECK(root_path.AppendRelativePath(changed_path, &relative_path));
} else {
// It is illegal for a source to notify of a change outside of its scope.
CHECK_EQ(root_path, changed_path);
}
NotifyOfChange(relative_path, error, change_info);
}
void FileSystemAccessLocalPathWatcher::OnUsageChange(size_t old_usage,
size_t new_usage) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK_EQ(current_usage_, old_usage);
if (new_usage == current_usage_) {
return;
}
current_usage_ = new_usage;
NotifyOfUsageChange(old_usage, new_usage);
}
} // namespace content
|