1 // Copyright 2015 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/filesystem/directory_impl.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10 #include <vector>
11
12 #include "base/files/file.h"
13 #include "base/files/file_enumerator.h"
14 #include "base/files/file_util.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/logging.h"
17 #include "build/build_config.h"
18 #include "components/services/filesystem/file_impl.h"
19 #include "components/services/filesystem/lock_table.h"
20 #include "components/services/filesystem/util.h"
21 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
22
23 namespace filesystem {
24
DirectoryImpl(base::FilePath directory_path,scoped_refptr<SharedTempDir> temp_dir,scoped_refptr<LockTable> lock_table)25 DirectoryImpl::DirectoryImpl(base::FilePath directory_path,
26 scoped_refptr<SharedTempDir> temp_dir,
27 scoped_refptr<LockTable> lock_table)
28 : directory_path_(directory_path),
29 temp_dir_(std::move(temp_dir)),
30 lock_table_(std::move(lock_table)) {}
31
~DirectoryImpl()32 DirectoryImpl::~DirectoryImpl() {}
33
Read(ReadCallback callback)34 void DirectoryImpl::Read(ReadCallback callback) {
35 std::vector<mojom::DirectoryEntryPtr> entries;
36 base::FileEnumerator directory_enumerator(
37 directory_path_, false,
38 base::FileEnumerator::DIRECTORIES | base::FileEnumerator::FILES);
39 for (base::FilePath name = directory_enumerator.Next(); !name.empty();
40 name = directory_enumerator.Next()) {
41 base::FileEnumerator::FileInfo info = directory_enumerator.GetInfo();
42 mojom::DirectoryEntryPtr entry = mojom::DirectoryEntry::New();
43 entry->type = info.IsDirectory() ? mojom::FsFileType::DIRECTORY
44 : mojom::FsFileType::REGULAR_FILE;
45 entry->name = info.GetName();
46 entries.push_back(std::move(entry));
47 }
48
49 std::move(callback).Run(base::File::Error::FILE_OK,
50 entries.empty()
51 ? base::nullopt
52 : base::make_optional(std::move(entries)));
53 }
54
55 // TODO(erg): Consider adding an implementation of Stat()/Touch() to the
56 // directory, too. Right now, the base::File abstractions do not really deal
57 // with directories properly, so these are broken for now.
58
59 // TODO(vtl): Move the implementation to a thread pool.
OpenFile(const std::string & raw_path,mojo::PendingReceiver<mojom::File> receiver,uint32_t open_flags,OpenFileCallback callback)60 void DirectoryImpl::OpenFile(const std::string& raw_path,
61 mojo::PendingReceiver<mojom::File> receiver,
62 uint32_t open_flags,
63 OpenFileCallback callback) {
64 base::FilePath path;
65 base::File::Error error = ValidatePath(raw_path, directory_path_, &path);
66 if (error != base::File::Error::FILE_OK) {
67 std::move(callback).Run(error);
68 return;
69 }
70
71 if (base::DirectoryExists(path)) {
72 // We must not return directories as files. In the file abstraction, we can
73 // fetch raw file descriptors over mojo pipes, and passing a file
74 // descriptor to a directory is a sandbox escape on Windows.
75 std::move(callback).Run(base::File::Error::FILE_ERROR_NOT_A_FILE);
76 return;
77 }
78
79 base::File base_file(path, open_flags);
80 if (!base_file.IsValid()) {
81 std::move(callback).Run(GetError(base_file));
82 return;
83 }
84
85 if (receiver) {
86 mojo::MakeSelfOwnedReceiver(
87 std::make_unique<FileImpl>(path, std::move(base_file), temp_dir_,
88 lock_table_),
89 std::move(receiver));
90 }
91 std::move(callback).Run(base::File::Error::FILE_OK);
92 }
93
OpenFileHandle(const std::string & raw_path,uint32_t open_flags,OpenFileHandleCallback callback)94 void DirectoryImpl::OpenFileHandle(const std::string& raw_path,
95 uint32_t open_flags,
96 OpenFileHandleCallback callback) {
97 base::File file = OpenFileHandleImpl(raw_path, open_flags);
98 base::File::Error error = GetError(file);
99 std::move(callback).Run(error, std::move(file));
100 }
101
OpenFileHandles(std::vector<mojom::FileOpenDetailsPtr> details,OpenFileHandlesCallback callback)102 void DirectoryImpl::OpenFileHandles(
103 std::vector<mojom::FileOpenDetailsPtr> details,
104 OpenFileHandlesCallback callback) {
105 std::vector<mojom::FileOpenResultPtr> results(details.size());
106 size_t i = 0;
107 for (const auto& detail : details) {
108 mojom::FileOpenResultPtr result(mojom::FileOpenResult::New());
109 result->path = detail->path;
110 result->file_handle = OpenFileHandleImpl(detail->path, detail->open_flags);
111 result->error = GetError(result->file_handle);
112 results[i++] = std::move(result);
113 }
114 std::move(callback).Run(std::move(results));
115 }
116
OpenDirectory(const std::string & raw_path,mojo::PendingReceiver<mojom::Directory> receiver,uint32_t open_flags,OpenDirectoryCallback callback)117 void DirectoryImpl::OpenDirectory(
118 const std::string& raw_path,
119 mojo::PendingReceiver<mojom::Directory> receiver,
120 uint32_t open_flags,
121 OpenDirectoryCallback callback) {
122 base::FilePath path;
123 base::File::Error error = ValidatePath(raw_path, directory_path_, &path);
124 if (error != base::File::Error::FILE_OK) {
125 std::move(callback).Run(error);
126 return;
127 }
128
129 if (!base::DirectoryExists(path)) {
130 if (base::PathExists(path)) {
131 std::move(callback).Run(base::File::Error::FILE_ERROR_NOT_A_DIRECTORY);
132 return;
133 }
134
135 if (!(open_flags & mojom::kFlagOpenAlways ||
136 open_flags & mojom::kFlagCreate)) {
137 // The directory doesn't exist, and we weren't passed parameters to
138 // create it.
139 std::move(callback).Run(base::File::Error::FILE_ERROR_NOT_FOUND);
140 return;
141 }
142
143 base::File::Error error;
144 if (!base::CreateDirectoryAndGetError(path, &error)) {
145 std::move(callback).Run(error);
146 return;
147 }
148 }
149
150 if (receiver) {
151 mojo::MakeSelfOwnedReceiver(
152 std::make_unique<DirectoryImpl>(path, temp_dir_, lock_table_),
153 std::move(receiver));
154 }
155
156 std::move(callback).Run(base::File::Error::FILE_OK);
157 }
158
Rename(const std::string & raw_old_path,const std::string & raw_new_path,RenameCallback callback)159 void DirectoryImpl::Rename(const std::string& raw_old_path,
160 const std::string& raw_new_path,
161 RenameCallback callback) {
162 base::FilePath old_path;
163 base::File::Error error =
164 ValidatePath(raw_old_path, directory_path_, &old_path);
165 if (error != base::File::Error::FILE_OK) {
166 std::move(callback).Run(error);
167 return;
168 }
169
170 base::FilePath new_path;
171 error = ValidatePath(raw_new_path, directory_path_, &new_path);
172 if (error != base::File::Error::FILE_OK) {
173 std::move(callback).Run(error);
174 return;
175 }
176
177 if (!base::Move(old_path, new_path)) {
178 std::move(callback).Run(base::File::Error::FILE_ERROR_FAILED);
179 return;
180 }
181
182 std::move(callback).Run(base::File::Error::FILE_OK);
183 }
184
Replace(const std::string & raw_old_path,const std::string & raw_new_path,ReplaceCallback callback)185 void DirectoryImpl::Replace(const std::string& raw_old_path,
186 const std::string& raw_new_path,
187 ReplaceCallback callback) {
188 base::FilePath old_path;
189 base::File::Error error =
190 ValidatePath(raw_old_path, directory_path_, &old_path);
191 if (error != base::File::Error::FILE_OK) {
192 std::move(callback).Run(error);
193 return;
194 }
195
196 base::FilePath new_path;
197 error = ValidatePath(raw_new_path, directory_path_, &new_path);
198 if (error != base::File::Error::FILE_OK) {
199 std::move(callback).Run(error);
200 return;
201 }
202
203 base::File::Error file_error;
204 if (!base::ReplaceFile(old_path, new_path, &file_error)) {
205 std::move(callback).Run(file_error);
206 return;
207 }
208
209 std::move(callback).Run(base::File::Error::FILE_OK);
210 }
211
Delete(const std::string & raw_path,uint32_t delete_flags,DeleteCallback callback)212 void DirectoryImpl::Delete(const std::string& raw_path,
213 uint32_t delete_flags,
214 DeleteCallback callback) {
215 base::FilePath path;
216 base::File::Error error = ValidatePath(raw_path, directory_path_, &path);
217 if (error != base::File::Error::FILE_OK) {
218 std::move(callback).Run(error);
219 return;
220 }
221
222 bool recursive = delete_flags & mojom::kDeleteFlagRecursive;
223 if (!base::DeleteFile(path, recursive)) {
224 std::move(callback).Run(base::File::Error::FILE_ERROR_FAILED);
225 return;
226 }
227
228 std::move(callback).Run(base::File::Error::FILE_OK);
229 }
230
Exists(const std::string & raw_path,ExistsCallback callback)231 void DirectoryImpl::Exists(const std::string& raw_path,
232 ExistsCallback callback) {
233 base::FilePath path;
234 base::File::Error error = ValidatePath(raw_path, directory_path_, &path);
235 if (error != base::File::Error::FILE_OK) {
236 std::move(callback).Run(error, false);
237 return;
238 }
239
240 bool exists = base::PathExists(path);
241 std::move(callback).Run(base::File::Error::FILE_OK, exists);
242 }
243
IsWritable(const std::string & raw_path,IsWritableCallback callback)244 void DirectoryImpl::IsWritable(const std::string& raw_path,
245 IsWritableCallback callback) {
246 base::FilePath path;
247 base::File::Error error = ValidatePath(raw_path, directory_path_, &path);
248 if (error != base::File::Error::FILE_OK) {
249 std::move(callback).Run(error, false);
250 return;
251 }
252
253 std::move(callback).Run(base::File::Error::FILE_OK,
254 base::PathIsWritable(path));
255 }
256
Flush(FlushCallback callback)257 void DirectoryImpl::Flush(FlushCallback callback) {
258 // On Windows no need to sync directories. Their metadata will be updated when
259 // files are created, without an explicit sync.
260 #if !defined(OS_WIN)
261 base::File file(directory_path_,
262 base::File::FLAG_OPEN | base::File::FLAG_READ);
263 if (!file.IsValid()) {
264 std::move(callback).Run(GetError(file));
265 return;
266 }
267
268 if (!file.Flush()) {
269 std::move(callback).Run(base::File::Error::FILE_ERROR_FAILED);
270 return;
271 }
272 #endif
273 std::move(callback).Run(base::File::Error::FILE_OK);
274 }
275
StatFile(const std::string & raw_path,StatFileCallback callback)276 void DirectoryImpl::StatFile(const std::string& raw_path,
277 StatFileCallback callback) {
278 base::FilePath path;
279 base::File::Error error = ValidatePath(raw_path, directory_path_, &path);
280 if (error != base::File::Error::FILE_OK) {
281 std::move(callback).Run(error, nullptr);
282 return;
283 }
284
285 base::File base_file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
286 if (!base_file.IsValid()) {
287 std::move(callback).Run(GetError(base_file), nullptr);
288 return;
289 }
290
291 base::File::Info info;
292 if (!base_file.GetInfo(&info)) {
293 std::move(callback).Run(base::File::Error::FILE_ERROR_FAILED, nullptr);
294 return;
295 }
296
297 std::move(callback).Run(base::File::Error::FILE_OK,
298 MakeFileInformation(info));
299 }
300
Clone(mojo::PendingReceiver<mojom::Directory> receiver)301 void DirectoryImpl::Clone(mojo::PendingReceiver<mojom::Directory> receiver) {
302 mojo::MakeSelfOwnedReceiver(
303 std::make_unique<DirectoryImpl>(directory_path_, temp_dir_, lock_table_),
304 std::move(receiver));
305 }
306
ReadEntireFile(const std::string & raw_path,ReadEntireFileCallback callback)307 void DirectoryImpl::ReadEntireFile(const std::string& raw_path,
308 ReadEntireFileCallback callback) {
309 base::FilePath path;
310 base::File::Error error = ValidatePath(raw_path, directory_path_, &path);
311 if (error != base::File::Error::FILE_OK) {
312 std::move(callback).Run(error, std::vector<uint8_t>());
313 return;
314 }
315
316 if (base::DirectoryExists(path)) {
317 std::move(callback).Run(base::File::Error::FILE_ERROR_NOT_A_FILE,
318 std::vector<uint8_t>());
319 return;
320 }
321
322 base::File base_file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
323 if (!base_file.IsValid()) {
324 std::move(callback).Run(GetError(base_file), std::vector<uint8_t>());
325 return;
326 }
327
328 std::vector<uint8_t> contents;
329 const int kBufferSize = 1 << 16;
330 std::unique_ptr<char[]> buf(new char[kBufferSize]);
331 int len;
332 while ((len = base_file.ReadAtCurrentPos(buf.get(), kBufferSize)) > 0)
333 contents.insert(contents.end(), buf.get(), buf.get() + len);
334
335 std::move(callback).Run(base::File::Error::FILE_OK, contents);
336 }
337
WriteFile(const std::string & raw_path,const std::vector<uint8_t> & data,WriteFileCallback callback)338 void DirectoryImpl::WriteFile(const std::string& raw_path,
339 const std::vector<uint8_t>& data,
340 WriteFileCallback callback) {
341 base::FilePath path;
342 base::File::Error error = ValidatePath(raw_path, directory_path_, &path);
343 if (error != base::File::Error::FILE_OK) {
344 std::move(callback).Run(error);
345 return;
346 }
347
348 if (base::DirectoryExists(path)) {
349 std::move(callback).Run(base::File::Error::FILE_ERROR_NOT_A_FILE);
350 return;
351 }
352
353 base::File base_file(path,
354 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
355 if (!base_file.IsValid()) {
356 std::move(callback).Run(GetError(base_file));
357 return;
358 }
359
360 // If we're given empty data, we don't write and just truncate the file.
361 if (data.size()) {
362 const int data_size = static_cast<int>(data.size());
363 if (base_file.Write(0, reinterpret_cast<const char*>(&data.front()),
364 data_size) == -1) {
365 std::move(callback).Run(GetError(base_file));
366 return;
367 }
368 }
369
370 std::move(callback).Run(base::File::Error::FILE_OK);
371 }
372
OpenFileHandleImpl(const std::string & raw_path,uint32_t open_flags)373 base::File DirectoryImpl::OpenFileHandleImpl(const std::string& raw_path,
374 uint32_t open_flags) {
375 base::FilePath path;
376 base::File::Error error = ValidatePath(raw_path, directory_path_, &path);
377 if (error != base::File::Error::FILE_OK)
378 return base::File(static_cast<base::File::Error>(error));
379
380 if (base::DirectoryExists(path)) {
381 // We must not return directories as files. In the file abstraction, we
382 // can fetch raw file descriptors over mojo pipes, and passing a file
383 // descriptor to a directory is a sandbox escape on Windows.
384 return base::File(base::File::FILE_ERROR_NOT_A_FILE);
385 }
386
387 return base::File(path, open_flags);
388 }
389
390 } // namespace filesystem
391