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