1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/services/storage/public/cpp/filesystem/filesystem_impl.h"
6
7 #include <set>
8 #include <vector>
9
10 #include "base/files/file.h"
11 #include "base/files/file_enumerator.h"
12 #include "base/files/file_util.h"
13 #include "base/logging.h"
14 #include "base/no_destructor.h"
15 #include "base/stl_util.h"
16 #include "base/synchronization/lock.h"
17 #include "build/build_config.h"
18 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
19
20 namespace storage {
21
22 namespace {
23
24 // Retains a mapping of lock file paths which have been locked by
25 // |FilesystemImpl::LockFile| and not yet released.
26 class LockTable {
27 public:
28 LockTable() = default;
29 LockTable(const LockTable&) = delete;
30 LockTable& operator=(const LockTable&) = delete;
31 ~LockTable() = default;
32
AddLock(const base::FilePath & path)33 bool AddLock(const base::FilePath& path) {
34 DCHECK(path.IsAbsolute());
35 base::AutoLock lock(lock_);
36 auto result = lock_paths_.insert(path.NormalizePathSeparators());
37 return result.second;
38 }
39
RemoveLock(const base::FilePath & path)40 void RemoveLock(const base::FilePath& path) {
41 const base::FilePath normalized_path = path.NormalizePathSeparators();
42 base::AutoLock lock(lock_);
43 DCHECK(base::Contains(lock_paths_, normalized_path));
44 lock_paths_.erase(normalized_path);
45 }
46
47 private:
48 base::Lock lock_;
49 std::set<base::FilePath> lock_paths_ GUARDED_BY(lock_);
50 };
51
52 // Get the global singleton instance of LockTable. This returned object is
53 // thread-safe.
GetLockTable()54 LockTable& GetLockTable() {
55 static base::NoDestructor<LockTable> table;
56 return *table;
57 }
58
59 class FileLockImpl : public mojom::FileLock {
60 public:
FileLockImpl(const base::FilePath & path,base::File file)61 FileLockImpl(const base::FilePath& path, base::File file)
62 : path_(path), file_(std::move(file)) {
63 DCHECK(file_.IsValid());
64 }
65
~FileLockImpl()66 ~FileLockImpl() override {
67 if (file_.IsValid())
68 GetLockTable().RemoveLock(path_);
69 }
70
71 // mojom::FileLock implementation:
Release(ReleaseCallback callback)72 void Release(ReleaseCallback callback) override {
73 if (!file_.IsValid()) {
74 std::move(callback).Run(base::File::FILE_ERROR_INVALID_OPERATION);
75 return;
76 }
77
78 #if defined(OS_FUCHSIA)
79 std::move(callback).Run(base::File::FILE_OK);
80 #else
81 std::move(callback).Run(file_.Unlock());
82 #endif
83 GetLockTable().RemoveLock(path_);
84 file_.Close();
85 }
86
87 private:
88 const base::FilePath path_;
89 base::File file_;
90 };
91
92 } // namespace
93
FilesystemImpl(const base::FilePath & root)94 FilesystemImpl::FilesystemImpl(const base::FilePath& root) : root_(root) {}
95
96 FilesystemImpl::~FilesystemImpl() = default;
97
Clone(mojo::PendingReceiver<mojom::Directory> receiver)98 void FilesystemImpl::Clone(mojo::PendingReceiver<mojom::Directory> receiver) {
99 mojo::MakeSelfOwnedReceiver(std::make_unique<FilesystemImpl>(root_),
100 std::move(receiver));
101 }
102
PathExists(const base::FilePath & path,PathExistsCallback callback)103 void FilesystemImpl::PathExists(const base::FilePath& path,
104 PathExistsCallback callback) {
105 std::move(callback).Run(base::PathExists(MakeAbsolute(path)));
106 }
107
GetEntries(const base::FilePath & path,mojom::GetEntriesMode mode,GetEntriesCallback callback)108 void FilesystemImpl::GetEntries(const base::FilePath& path,
109 mojom::GetEntriesMode mode,
110 GetEntriesCallback callback) {
111 const base::FilePath full_path = MakeAbsolute(path);
112 FileErrorOr<std::vector<base::FilePath>> result =
113 GetDirectoryEntries(full_path, mode);
114 if (result.is_error()) {
115 std::move(callback).Run(result.error(), std::vector<base::FilePath>());
116 return;
117 }
118
119 // Fix up the absolute paths to be relative to |path|.
120 std::vector<base::FilePath> entries;
121 std::vector<base::FilePath::StringType> root_components;
122 full_path.GetComponents(&root_components);
123 const size_t num_components_to_strip = root_components.size();
124 for (const auto& entry : result.value()) {
125 std::vector<base::FilePath::StringType> components;
126 entry.GetComponents(&components);
127 base::FilePath relative_path;
128 for (size_t i = num_components_to_strip; i < components.size(); ++i)
129 relative_path = relative_path.Append(components[i]);
130 entries.push_back(std::move(relative_path));
131 }
132 std::move(callback).Run(base::File::FILE_OK, entries);
133 }
134
OpenFile(const base::FilePath & path,mojom::FileOpenMode mode,mojom::FileReadAccess read_access,mojom::FileWriteAccess write_access,OpenFileCallback callback)135 void FilesystemImpl::OpenFile(const base::FilePath& path,
136 mojom::FileOpenMode mode,
137 mojom::FileReadAccess read_access,
138 mojom::FileWriteAccess write_access,
139 OpenFileCallback callback) {
140 uint32_t flags = 0;
141 switch (mode) {
142 case mojom::FileOpenMode::kOpenIfExists:
143 flags |= base::File::FLAG_OPEN;
144 break;
145 case mojom::FileOpenMode::kCreateAndOpenOnlyIfNotExists:
146 flags |= base::File::FLAG_CREATE;
147 break;
148 case mojom::FileOpenMode::kAlwaysOpen:
149 flags |= base::File::FLAG_OPEN_ALWAYS;
150 break;
151 case mojom::FileOpenMode::kAlwaysCreate:
152 flags |= base::File::FLAG_CREATE_ALWAYS;
153 break;
154 case mojom::FileOpenMode::kOpenIfExistsAndTruncate:
155 flags |= base::File::FLAG_OPEN_TRUNCATED;
156 break;
157 default:
158 NOTREACHED();
159 return;
160 }
161
162 switch (read_access) {
163 case mojom::FileReadAccess::kReadNotAllowed:
164 break;
165 case mojom::FileReadAccess::kReadAllowed:
166 flags |= base::File::FLAG_READ;
167 break;
168 default:
169 NOTREACHED();
170 break;
171 }
172
173 switch (write_access) {
174 case mojom::FileWriteAccess::kWriteNotAllowed:
175 break;
176 case mojom::FileWriteAccess::kWriteAllowed:
177 flags |= base::File::FLAG_WRITE;
178 break;
179 case mojom::FileWriteAccess::kAppendOnly:
180 flags |= base::File::FLAG_APPEND;
181 break;
182 default:
183 NOTREACHED();
184 break;
185 }
186
187 const base::FilePath full_path = MakeAbsolute(path);
188 base::File file(full_path, flags);
189 base::File::Error error = base::File::FILE_OK;
190 if (!file.IsValid())
191 error = file.error_details();
192 std::move(callback).Run(error, std::move(file));
193 }
194
RemoveFile(const base::FilePath & path,RemoveFileCallback callback)195 void FilesystemImpl::RemoveFile(const base::FilePath& path,
196 RemoveFileCallback callback) {
197 std::move(callback).Run(
198 base::DeleteFile(MakeAbsolute(path), /*recursive=*/false));
199 }
200
CreateDirectory(const base::FilePath & path,CreateDirectoryCallback callback)201 void FilesystemImpl::CreateDirectory(const base::FilePath& path,
202 CreateDirectoryCallback callback) {
203 base::File::Error error = base::File::FILE_OK;
204 base::CreateDirectoryAndGetError(MakeAbsolute(path), &error);
205 std::move(callback).Run(error);
206 }
207
RemoveDirectory(const base::FilePath & path,RemoveDirectoryCallback callback)208 void FilesystemImpl::RemoveDirectory(const base::FilePath& path,
209 RemoveDirectoryCallback callback) {
210 const base::FilePath full_path = MakeAbsolute(path);
211 if (!base::DirectoryExists(full_path)) {
212 std::move(callback).Run(false);
213 return;
214 }
215
216 std::move(callback).Run(base::DeleteFile(full_path, /*recursive=*/false));
217 }
218
GetFileInfo(const base::FilePath & path,GetFileInfoCallback callback)219 void FilesystemImpl::GetFileInfo(const base::FilePath& path,
220 GetFileInfoCallback callback) {
221 base::File::Info info;
222 if (base::GetFileInfo(MakeAbsolute(path), &info))
223 std::move(callback).Run(std::move(info));
224 else
225 std::move(callback).Run(base::nullopt);
226 }
227
RenameFile(const base::FilePath & old_path,const base::FilePath & new_path,RenameFileCallback callback)228 void FilesystemImpl::RenameFile(const base::FilePath& old_path,
229 const base::FilePath& new_path,
230 RenameFileCallback callback) {
231 base::File::Error error = base::File::FILE_OK;
232 base::ReplaceFile(MakeAbsolute(old_path), MakeAbsolute(new_path), &error);
233 std::move(callback).Run(error);
234 }
235
LockFile(const base::FilePath & path,LockFileCallback callback)236 void FilesystemImpl::LockFile(const base::FilePath& path,
237 LockFileCallback callback) {
238 FileErrorOr<base::File> result = LockFileLocal(MakeAbsolute(path));
239 if (result.is_error()) {
240 std::move(callback).Run(result.error(), mojo::NullRemote());
241 return;
242 }
243
244 mojo::PendingRemote<mojom::FileLock> lock;
245 mojo::MakeSelfOwnedReceiver(
246 std::make_unique<FileLockImpl>(MakeAbsolute(path),
247 std::move(result.value())),
248 lock.InitWithNewPipeAndPassReceiver());
249 std::move(callback).Run(base::File::FILE_OK, std::move(lock));
250 }
251
252 // static
LockFileLocal(const base::FilePath & path)253 FileErrorOr<base::File> FilesystemImpl::LockFileLocal(
254 const base::FilePath& path) {
255 DCHECK(path.IsAbsolute());
256 base::File file(path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ |
257 base::File::FLAG_WRITE);
258 if (!file.IsValid())
259 return file.error_details();
260
261 if (!GetLockTable().AddLock(path))
262 return base::File::FILE_ERROR_IN_USE;
263
264 #if !defined(OS_FUCHSIA)
265 base::File::Error error = file.Lock();
266 if (error != base::File::FILE_OK)
267 return error;
268 #endif
269
270 return file;
271 }
272
273 // static
UnlockFileLocal(const base::FilePath & path)274 void FilesystemImpl::UnlockFileLocal(const base::FilePath& path) {
275 GetLockTable().RemoveLock(path);
276 }
277
278 // static
GetDirectoryEntries(const base::FilePath & path,mojom::GetEntriesMode mode)279 FileErrorOr<std::vector<base::FilePath>> FilesystemImpl::GetDirectoryEntries(
280 const base::FilePath& path,
281 mojom::GetEntriesMode mode) {
282 DCHECK(path.IsAbsolute());
283 int file_types = base::FileEnumerator::FILES;
284 if (mode == mojom::GetEntriesMode::kFilesAndDirectories)
285 file_types |= base::FileEnumerator::DIRECTORIES;
286 base::FileEnumerator enumerator(
287 path, /*recursive=*/false, file_types,
288 /*pattern=*/base::FilePath::StringType(),
289 base::FileEnumerator::FolderSearchPolicy::ALL,
290 base::FileEnumerator::ErrorPolicy::STOP_ENUMERATION);
291 std::vector<base::FilePath> entries;
292 for (base::FilePath path = enumerator.Next(); !path.empty();
293 path = enumerator.Next()) {
294 entries.push_back(path);
295 }
296 if (enumerator.GetError() != base::File::FILE_OK)
297 return enumerator.GetError();
298 return entries;
299 }
300
MakeAbsolute(const base::FilePath & path) const301 base::FilePath FilesystemImpl::MakeAbsolute(const base::FilePath& path) const {
302 // The DCHECK is a reasonable assertion: this object is only called into via
303 // Mojo, and type-map traits for |storage.mojom.StrictRelativePath| ensure
304 // that messages can only reach this object if they carry strictly relative
305 // paths.
306 DCHECK(!path.IsAbsolute());
307 return root_.Append(path);
308 }
309
310 } // namespace storage
311