1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "third_party/blink/renderer/modules/filesystem/dom_file_system_base.h"
32 
33 #include <memory>
34 #include "third_party/blink/public/platform/platform.h"
35 #include "third_party/blink/public/platform/web_security_origin.h"
36 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
37 #include "third_party/blink/renderer/core/fileapi/file.h"
38 #include "third_party/blink/renderer/core/fileapi/file_error.h"
39 #include "third_party/blink/renderer/modules/filesystem/directory_entry.h"
40 #include "third_party/blink/renderer/modules/filesystem/directory_reader_base.h"
41 #include "third_party/blink/renderer/modules/filesystem/dom_file_path.h"
42 #include "third_party/blink/renderer/modules/filesystem/entry.h"
43 #include "third_party/blink/renderer/modules/filesystem/entry_base.h"
44 #include "third_party/blink/renderer/modules/filesystem/file_system_callbacks.h"
45 #include "third_party/blink/renderer/modules/filesystem/file_system_dispatcher.h"
46 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
47 #include "third_party/blink/renderer/platform/wtf/assertions.h"
48 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
49 #include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
50 
51 namespace blink {
52 
53 const char DOMFileSystemBase::kPersistentPathPrefix[] = "persistent";
54 const char DOMFileSystemBase::kTemporaryPathPrefix[] = "temporary";
55 const char DOMFileSystemBase::kIsolatedPathPrefix[] = "isolated";
56 const char DOMFileSystemBase::kExternalPathPrefix[] = "external";
57 
DOMFileSystemBase(ExecutionContext * context,const String & name,mojom::blink::FileSystemType type,const KURL & root_url)58 DOMFileSystemBase::DOMFileSystemBase(ExecutionContext* context,
59                                      const String& name,
60                                      mojom::blink::FileSystemType type,
61                                      const KURL& root_url)
62     : context_(context),
63       name_(name),
64       type_(type),
65       filesystem_root_url_(root_url),
66       clonable_(false) {}
67 
68 DOMFileSystemBase::~DOMFileSystemBase() = default;
69 
Trace(Visitor * visitor)70 void DOMFileSystemBase::Trace(Visitor* visitor) {
71   visitor->Trace(context_);
72   ScriptWrappable::Trace(visitor);
73 }
74 
GetSecurityOrigin() const75 const SecurityOrigin* DOMFileSystemBase::GetSecurityOrigin() const {
76   return context_->GetSecurityOrigin();
77 }
78 
IsValidType(mojom::blink::FileSystemType type)79 bool DOMFileSystemBase::IsValidType(mojom::blink::FileSystemType type) {
80   return type == mojom::blink::FileSystemType::kTemporary ||
81          type == mojom::blink::FileSystemType::kPersistent ||
82          type == mojom::blink::FileSystemType::kIsolated ||
83          type == mojom::blink::FileSystemType::kExternal;
84 }
85 
CreateFileSystemRootURL(const String & origin,mojom::blink::FileSystemType type)86 KURL DOMFileSystemBase::CreateFileSystemRootURL(
87     const String& origin,
88     mojom::blink::FileSystemType type) {
89   String type_string;
90   if (type == mojom::blink::FileSystemType::kTemporary)
91     type_string = kTemporaryPathPrefix;
92   else if (type == mojom::blink::FileSystemType::kPersistent)
93     type_string = kPersistentPathPrefix;
94   else if (type == mojom::blink::FileSystemType::kExternal)
95     type_string = kExternalPathPrefix;
96   else
97     return KURL();
98 
99   String result = "filesystem:" + origin + "/" + type_string + "/";
100   return KURL(result);
101 }
102 
SupportsToURL() const103 bool DOMFileSystemBase::SupportsToURL() const {
104   DCHECK(IsValidType(type_));
105   return type_ != mojom::blink::FileSystemType::kIsolated;
106 }
107 
CreateFileSystemURL(const EntryBase * entry) const108 KURL DOMFileSystemBase::CreateFileSystemURL(const EntryBase* entry) const {
109   return CreateFileSystemURL(entry->fullPath());
110 }
111 
CreateFileSystemURL(const String & full_path) const112 KURL DOMFileSystemBase::CreateFileSystemURL(const String& full_path) const {
113   DCHECK(DOMFilePath::IsAbsolute(full_path));
114 
115   if (GetType() == mojom::blink::FileSystemType::kExternal) {
116     // For external filesystem originString could be different from what we have
117     // in m_filesystemRootURL.
118     StringBuilder result;
119     result.Append("filesystem:");
120     result.Append(GetSecurityOrigin()->ToString());
121     result.Append('/');
122     result.Append(kExternalPathPrefix);
123     result.Append(filesystem_root_url_.GetPath());
124     // Remove the extra leading slash.
125     result.Append(EncodeWithURLEscapeSequences(full_path.Substring(1)));
126     return KURL(result.ToString());
127   }
128 
129   // For regular types we can just append the entry's fullPath to the
130   // m_filesystemRootURL that should look like
131   // 'filesystem:<origin>/<typePrefix>'.
132   DCHECK(!filesystem_root_url_.IsEmpty());
133   KURL url = filesystem_root_url_;
134   // Remove the extra leading slash.
135   url.SetPath(url.GetPath() +
136               EncodeWithURLEscapeSequences(full_path.Substring(1)));
137   return url;
138 }
139 
PathToAbsolutePath(mojom::blink::FileSystemType type,const EntryBase * base,String path,String & absolute_path)140 bool DOMFileSystemBase::PathToAbsolutePath(mojom::blink::FileSystemType type,
141                                            const EntryBase* base,
142                                            String path,
143                                            String& absolute_path) {
144   DCHECK(base);
145 
146   if (!DOMFilePath::IsAbsolute(path))
147     path = DOMFilePath::Append(base->fullPath(), path);
148   absolute_path = DOMFilePath::RemoveExtraParentReferences(path);
149 
150   return (type != mojom::blink::FileSystemType::kTemporary &&
151           type != mojom::blink::FileSystemType::kPersistent) ||
152          DOMFilePath::IsValidPath(absolute_path);
153 }
154 
PathPrefixToFileSystemType(const String & path_prefix,mojom::blink::FileSystemType & type)155 bool DOMFileSystemBase::PathPrefixToFileSystemType(
156     const String& path_prefix,
157     mojom::blink::FileSystemType& type) {
158   if (path_prefix == kTemporaryPathPrefix) {
159     type = mojom::blink::FileSystemType::kTemporary;
160     return true;
161   }
162 
163   if (path_prefix == kPersistentPathPrefix) {
164     type = mojom::blink::FileSystemType::kPersistent;
165     return true;
166   }
167 
168   if (path_prefix == kExternalPathPrefix) {
169     type = mojom::blink::FileSystemType::kExternal;
170     return true;
171   }
172 
173   return false;
174 }
175 
CreateFile(const FileMetadata & metadata,const KURL & file_system_url,mojom::blink::FileSystemType type,const String name)176 File* DOMFileSystemBase::CreateFile(const FileMetadata& metadata,
177                                     const KURL& file_system_url,
178                                     mojom::blink::FileSystemType type,
179                                     const String name) {
180   // For regular filesystem types (temporary or persistent), we should not cache
181   // file metadata as it could change File semantics.  For other filesystem
182   // types (which could be platform-specific ones), there's a chance that the
183   // files are on remote filesystem.  If the port has returned metadata just
184   // pass it to File constructor (so we may cache the metadata).
185   // If |metadata.platform_path|, filesystem will decide about the actual
186   // storage location based on the url.
187   // FIXME: We should use the snapshot metadata for all files.
188   // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17746
189   if (!metadata.platform_path.IsEmpty() &&
190       (type == mojom::blink::FileSystemType::kTemporary ||
191        type == mojom::blink::FileSystemType::kPersistent))
192     return File::CreateForFileSystemFile(metadata.platform_path, name);
193 
194   const File::UserVisibility user_visibility =
195       (type == mojom::blink::FileSystemType::kExternal)
196           ? File::kIsUserVisible
197           : File::kIsNotUserVisible;
198 
199   if (!metadata.platform_path.IsEmpty()) {
200     // If the platformPath in the returned metadata is given, we create a File
201     // object for the snapshot path.
202     return File::CreateForFileSystemFile(name, metadata, user_visibility);
203   } else {
204     // Otherwise we create a File object for the fileSystemURL.
205     return File::CreateForFileSystemFile(file_system_url, metadata,
206                                          user_visibility);
207   }
208 }
209 
GetMetadata(const EntryBase * entry,MetadataCallbacks::SuccessCallback success_callback,MetadataCallbacks::ErrorCallback error_callback,SynchronousType synchronous_type)210 void DOMFileSystemBase::GetMetadata(
211     const EntryBase* entry,
212     MetadataCallbacks::SuccessCallback success_callback,
213     MetadataCallbacks::ErrorCallback error_callback,
214     SynchronousType synchronous_type) {
215   auto callbacks = std::make_unique<MetadataCallbacks>(
216       std::move(success_callback), std::move(error_callback), context_, this);
217   FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
218 
219   if (synchronous_type == kSynchronous) {
220     dispatcher.ReadMetadataSync(CreateFileSystemURL(entry),
221                                 std::move(callbacks));
222   } else {
223     dispatcher.ReadMetadata(CreateFileSystemURL(entry), std::move(callbacks));
224   }
225 }
226 
VerifyAndGetDestinationPathForCopyOrMove(const EntryBase * source,EntryBase * parent,const String & new_name,String & destination_path)227 static bool VerifyAndGetDestinationPathForCopyOrMove(const EntryBase* source,
228                                                      EntryBase* parent,
229                                                      const String& new_name,
230                                                      String& destination_path) {
231   DCHECK(source);
232 
233   if (!parent || !parent->isDirectory())
234     return false;
235 
236   if (!new_name.IsEmpty() && !DOMFilePath::IsValidName(new_name))
237     return false;
238 
239   const bool is_same_file_system =
240       (*source->filesystem() == *parent->filesystem());
241 
242   // It is an error to try to copy or move an entry inside itself at any depth
243   // if it is a directory.
244   if (source->isDirectory() && is_same_file_system &&
245       DOMFilePath::IsParentOf(source->fullPath(), parent->fullPath()))
246     return false;
247 
248   // It is an error to copy or move an entry into its parent if a name different
249   // from its current one isn't provided.
250   if (is_same_file_system &&
251       (new_name.IsEmpty() || source->name() == new_name) &&
252       DOMFilePath::GetDirectory(source->fullPath()) == parent->fullPath())
253     return false;
254 
255   destination_path = parent->fullPath();
256   if (!new_name.IsEmpty())
257     destination_path = DOMFilePath::Append(destination_path, new_name);
258   else
259     destination_path = DOMFilePath::Append(destination_path, source->name());
260 
261   return true;
262 }
263 
Move(const EntryBase * source,EntryBase * parent,const String & new_name,EntryCallbacks::SuccessCallback success_callback,EntryCallbacks::ErrorCallback error_callback,SynchronousType synchronous_type)264 void DOMFileSystemBase::Move(const EntryBase* source,
265                              EntryBase* parent,
266                              const String& new_name,
267                              EntryCallbacks::SuccessCallback success_callback,
268                              EntryCallbacks::ErrorCallback error_callback,
269                              SynchronousType synchronous_type) {
270   String destination_path;
271   if (!VerifyAndGetDestinationPathForCopyOrMove(source, parent, new_name,
272                                                 destination_path)) {
273     ReportError(std::move(error_callback),
274                 base::File::FILE_ERROR_INVALID_OPERATION);
275     return;
276   }
277 
278   auto callbacks = std::make_unique<EntryCallbacks>(
279       std::move(success_callback), std::move(error_callback), context_,
280       parent->filesystem(), destination_path, source->isDirectory());
281 
282   FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
283   const KURL& src = CreateFileSystemURL(source);
284   const KURL& dest =
285       parent->filesystem()->CreateFileSystemURL(destination_path);
286   if (synchronous_type == kSynchronous)
287     dispatcher.MoveSync(src, dest, std::move(callbacks));
288   else
289     dispatcher.Move(src, dest, std::move(callbacks));
290 }
291 
Copy(const EntryBase * source,EntryBase * parent,const String & new_name,EntryCallbacks::SuccessCallback success_callback,EntryCallbacks::ErrorCallback error_callback,SynchronousType synchronous_type)292 void DOMFileSystemBase::Copy(const EntryBase* source,
293                              EntryBase* parent,
294                              const String& new_name,
295                              EntryCallbacks::SuccessCallback success_callback,
296                              EntryCallbacks::ErrorCallback error_callback,
297                              SynchronousType synchronous_type) {
298   String destination_path;
299   if (!VerifyAndGetDestinationPathForCopyOrMove(source, parent, new_name,
300                                                 destination_path)) {
301     ReportError(std::move(error_callback),
302                 base::File::FILE_ERROR_INVALID_OPERATION);
303     return;
304   }
305 
306   auto callbacks = std::make_unique<EntryCallbacks>(
307       std::move(success_callback), std::move(error_callback), context_,
308       parent->filesystem(), destination_path, source->isDirectory());
309 
310   const KURL& src = CreateFileSystemURL(source);
311   const KURL& dest =
312       parent->filesystem()->CreateFileSystemURL(destination_path);
313   FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
314   if (synchronous_type == kSynchronous)
315     dispatcher.CopySync(src, dest, std::move(callbacks));
316   else
317     dispatcher.Copy(src, dest, std::move(callbacks));
318 }
319 
Remove(const EntryBase * entry,VoidCallbacks::SuccessCallback success_callback,ErrorCallback error_callback,SynchronousType synchronous_type)320 void DOMFileSystemBase::Remove(const EntryBase* entry,
321                                VoidCallbacks::SuccessCallback success_callback,
322                                ErrorCallback error_callback,
323                                SynchronousType synchronous_type) {
324   DCHECK(entry);
325   // We don't allow calling remove() on the root directory.
326   if (entry->fullPath() == String(DOMFilePath::kRoot)) {
327     ReportError(std::move(error_callback),
328                 base::File::FILE_ERROR_INVALID_OPERATION);
329     return;
330   }
331 
332   auto callbacks = std::make_unique<VoidCallbacks>(
333       std::move(success_callback), std::move(error_callback), context_, this);
334   const KURL& url = CreateFileSystemURL(entry);
335   FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
336   if (synchronous_type == kSynchronous)
337     dispatcher.RemoveSync(url, /*recursive=*/false, std::move(callbacks));
338   else
339     dispatcher.Remove(url, /*recursive=*/false, std::move(callbacks));
340 }
341 
RemoveRecursively(const EntryBase * entry,VoidCallbacks::SuccessCallback success_callback,ErrorCallback error_callback,SynchronousType synchronous_type)342 void DOMFileSystemBase::RemoveRecursively(
343     const EntryBase* entry,
344     VoidCallbacks::SuccessCallback success_callback,
345     ErrorCallback error_callback,
346     SynchronousType synchronous_type) {
347   DCHECK(entry);
348   DCHECK(entry->isDirectory());
349   // We don't allow calling remove() on the root directory.
350   if (entry->fullPath() == String(DOMFilePath::kRoot)) {
351     ReportError(std::move(error_callback),
352                 base::File::FILE_ERROR_INVALID_OPERATION);
353     return;
354   }
355 
356   auto callbacks = std::make_unique<VoidCallbacks>(
357       std::move(success_callback), std::move(error_callback), context_, this);
358   const KURL& url = CreateFileSystemURL(entry);
359   FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
360   if (synchronous_type == kSynchronous)
361     dispatcher.RemoveSync(url, /*recursive=*/true, std::move(callbacks));
362   else
363     dispatcher.Remove(url, /*recursive=*/true, std::move(callbacks));
364 }
365 
GetParent(const EntryBase * entry,EntryCallbacks::SuccessCallback success_callback,EntryCallbacks::ErrorCallback error_callback)366 void DOMFileSystemBase::GetParent(
367     const EntryBase* entry,
368     EntryCallbacks::SuccessCallback success_callback,
369     EntryCallbacks::ErrorCallback error_callback) {
370   DCHECK(entry);
371   String path = DOMFilePath::GetDirectory(entry->fullPath());
372 
373   FileSystemDispatcher::From(context_).Exists(
374       CreateFileSystemURL(path), /*is_directory=*/true,
375       std::make_unique<EntryCallbacks>(std::move(success_callback),
376                                        std::move(error_callback), context_,
377                                        this, path, true));
378 }
379 
GetFile(const EntryBase * entry,const String & path,const FileSystemFlags * flags,EntryCallbacks::SuccessCallback success_callback,EntryCallbacks::ErrorCallback error_callback,SynchronousType synchronous_type)380 void DOMFileSystemBase::GetFile(
381     const EntryBase* entry,
382     const String& path,
383     const FileSystemFlags* flags,
384     EntryCallbacks::SuccessCallback success_callback,
385     EntryCallbacks::ErrorCallback error_callback,
386     SynchronousType synchronous_type) {
387   String absolute_path;
388   if (!PathToAbsolutePath(type_, entry, path, absolute_path)) {
389     ReportError(std::move(error_callback),
390                 base::File::FILE_ERROR_INVALID_OPERATION);
391     return;
392   }
393 
394   auto callbacks = std::make_unique<EntryCallbacks>(
395       std::move(success_callback), std::move(error_callback), context_, this,
396       absolute_path, false);
397   const KURL& url = CreateFileSystemURL(absolute_path);
398   FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
399 
400   if (flags->createFlag()) {
401     if (synchronous_type == kSynchronous)
402       dispatcher.CreateFileSync(url, flags->exclusive(), std::move(callbacks));
403     else
404       dispatcher.CreateFile(url, flags->exclusive(), std::move(callbacks));
405   } else {
406     if (synchronous_type == kSynchronous) {
407       dispatcher.ExistsSync(url, /*is_directory=*/false, std::move(callbacks));
408     } else {
409       dispatcher.Exists(url, /*is_directory=*/false, std::move(callbacks));
410     }
411   }
412 }
413 
GetDirectory(const EntryBase * entry,const String & path,const FileSystemFlags * flags,EntryCallbacks::SuccessCallback success_callback,EntryCallbacks::ErrorCallback error_callback,SynchronousType synchronous_type)414 void DOMFileSystemBase::GetDirectory(
415     const EntryBase* entry,
416     const String& path,
417     const FileSystemFlags* flags,
418     EntryCallbacks::SuccessCallback success_callback,
419     EntryCallbacks::ErrorCallback error_callback,
420     SynchronousType synchronous_type) {
421   String absolute_path;
422   if (!PathToAbsolutePath(type_, entry, path, absolute_path)) {
423     ReportError(std::move(error_callback),
424                 base::File::FILE_ERROR_INVALID_OPERATION);
425     return;
426   }
427 
428   auto callbacks = std::make_unique<EntryCallbacks>(
429       std::move(success_callback), std::move(error_callback), context_, this,
430       absolute_path, true);
431   const KURL& url = CreateFileSystemURL(absolute_path);
432   FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
433 
434   if (flags->createFlag()) {
435     if (synchronous_type == kSynchronous) {
436       dispatcher.CreateDirectorySync(url, flags->exclusive(),
437                                      /*recursive=*/false, std::move(callbacks));
438     } else {
439       dispatcher.CreateDirectory(url, flags->exclusive(), /*recursive=*/false,
440                                  std::move(callbacks));
441     }
442   } else {
443     if (synchronous_type == kSynchronous) {
444       dispatcher.ExistsSync(url, /*is_directory=*/true, std::move(callbacks));
445     } else {
446       dispatcher.Exists(url, /*is_directory=*/true, std::move(callbacks));
447     }
448   }
449 }
450 
ReadDirectory(DirectoryReaderBase * reader,const String & path,const EntriesCallbacks::SuccessCallback & success_callback,EntriesCallbacks::ErrorCallback error_callback,SynchronousType synchronous_type)451 void DOMFileSystemBase::ReadDirectory(
452     DirectoryReaderBase* reader,
453     const String& path,
454     const EntriesCallbacks::SuccessCallback& success_callback,
455     EntriesCallbacks::ErrorCallback error_callback,
456     SynchronousType synchronous_type) {
457   DCHECK(DOMFilePath::IsAbsolute(path));
458 
459   auto callbacks = std::make_unique<EntriesCallbacks>(
460       success_callback, std::move(error_callback), context_, reader, path);
461   FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
462   const KURL& url = CreateFileSystemURL(path);
463   if (synchronous_type == kSynchronous) {
464     dispatcher.ReadDirectorySync(url, std::move(callbacks));
465   } else {
466     dispatcher.ReadDirectory(url, std::move(callbacks));
467   }
468 }
469 
470 }  // namespace blink
471