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