1// Copyright 2014 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 "fake_ppapi/fake_pepper_interface_html5_fs.h"
6
7#include <string.h>
8
9#include <algorithm>
10
11#include <ppapi/c/pp_completion_callback.h>
12#include <ppapi/c/pp_errors.h>
13
14#include "gtest/gtest.h"
15
16namespace {
17
18class FakeInstanceResource : public FakeResource {
19 public:
20  FakeInstanceResource() : filesystem_template(NULL) {}
21  static const char* classname() { return "FakeInstanceResource"; }
22
23  FakeHtml5FsFilesystem* filesystem_template;  // Weak reference.
24};
25
26class FakeFileSystemResource : public FakeResource {
27 public:
28  FakeFileSystemResource() : filesystem(NULL), opened(false) {}
29  ~FakeFileSystemResource() { delete filesystem; }
30  static const char* classname() { return "FakeFileSystemResource"; }
31
32  FakeHtml5FsFilesystem* filesystem;  // Owned.
33  bool opened;
34};
35
36class FakeFileRefResource : public FakeResource {
37 public:
38  FakeFileRefResource() : filesystem(NULL) {}
39  static const char* classname() { return "FakeFileRefResource"; }
40
41  FakeHtml5FsFilesystem* filesystem;  // Weak reference.
42  FakeHtml5FsFilesystem::Path path;
43};
44
45class FakeFileIoResource : public FakeResource {
46 public:
47  FakeFileIoResource() : node(NULL), open_flags(0) {}
48  static const char* classname() { return "FakeFileIoResource"; }
49
50  FakeHtml5FsNode* node;  // Weak reference.
51  int32_t open_flags;
52};
53
54// Helper function to call the completion callback if it is defined (an
55// asynchronous call), or return the result directly if it isn't (a synchronous
56// call).
57//
58// Use like this:
59//   if (<some error condition>)
60//     return RunCompletionCallback(callback, PP_ERROR_FUBAR);
61//
62//   /* Everything worked OK */
63//   return RunCompletionCallback(callback, PP_OK);
64int32_t RunCompletionCallback(PP_CompletionCallback* callback, int32_t result) {
65  if (callback->func) {
66    PP_RunCompletionCallback(callback, result);
67    return PP_OK_COMPLETIONPENDING;
68  }
69  return result;
70}
71
72}  // namespace
73
74FakeHtml5FsNode::FakeHtml5FsNode(const PP_FileInfo& info) : info_(info) {}
75
76FakeHtml5FsNode::FakeHtml5FsNode(const PP_FileInfo& info,
77                                 const std::vector<uint8_t>& contents)
78    : info_(info), contents_(contents) {}
79
80FakeHtml5FsNode::FakeHtml5FsNode(const PP_FileInfo& info,
81                                 const std::string& contents)
82    : info_(info) {
83  std::copy(contents.begin(), contents.end(), std::back_inserter(contents_));
84}
85
86int32_t FakeHtml5FsNode::Read(int64_t offset,
87                              char* buffer,
88                              int32_t bytes_to_read) {
89  if (offset < 0)
90    return PP_ERROR_FAILED;
91
92  bytes_to_read =
93      std::max(0, std::min<int32_t>(bytes_to_read, contents_.size() - offset));
94  memcpy(buffer, contents_.data() + offset, bytes_to_read);
95  return bytes_to_read;
96}
97
98int32_t FakeHtml5FsNode::Write(int64_t offset,
99                               const char* buffer,
100                               int32_t bytes_to_write) {
101  if (offset < 0)
102    return PP_ERROR_FAILED;
103
104  size_t new_size = offset + bytes_to_write;
105  if (new_size > contents_.size())
106    contents_.resize(new_size);
107
108  memcpy(contents_.data() + offset, buffer, bytes_to_write);
109  info_.size = new_size;
110  return bytes_to_write;
111}
112
113int32_t FakeHtml5FsNode::Append(const char* buffer, int32_t bytes_to_write) {
114  return Write(contents_.size(), buffer, bytes_to_write);
115}
116
117int32_t FakeHtml5FsNode::SetLength(int64_t length) {
118  contents_.resize(length);
119  info_.size = length;
120  return PP_OK;
121}
122
123void FakeHtml5FsNode::GetInfo(PP_FileInfo* out_info) { *out_info = info_; }
124
125bool FakeHtml5FsNode::IsRegular() const {
126  return info_.type == PP_FILETYPE_REGULAR;
127}
128
129bool FakeHtml5FsNode::IsDirectory() const {
130  return info_.type == PP_FILETYPE_DIRECTORY;
131}
132
133FakeHtml5FsFilesystem::FakeHtml5FsFilesystem()
134    : filesystem_type_(PP_FILESYSTEMTYPE_INVALID) {
135  Clear();
136}
137
138FakeHtml5FsFilesystem::FakeHtml5FsFilesystem(PP_FileSystemType type)
139    : filesystem_type_(type) {
140  Clear();
141}
142
143FakeHtml5FsFilesystem::FakeHtml5FsFilesystem(
144    const FakeHtml5FsFilesystem& filesystem,
145    PP_FileSystemType type)
146    : node_map_(filesystem.node_map_), filesystem_type_(type) {}
147
148void FakeHtml5FsFilesystem::Clear() {
149  node_map_.clear();
150  // Always have a root node.
151  AddDirectory("/", NULL);
152}
153
154bool FakeHtml5FsFilesystem::AddEmptyFile(const Path& path,
155                                         FakeHtml5FsNode** out_node) {
156  return AddFile(path, std::vector<uint8_t>(), out_node);
157}
158
159bool FakeHtml5FsFilesystem::AddFile(const Path& path,
160                                    const std::string& contents,
161                                    FakeHtml5FsNode** out_node) {
162  std::vector<uint8_t> data;
163  std::copy(contents.begin(), contents.end(), std::back_inserter(data));
164  return AddFile(path, data, out_node);
165}
166
167bool FakeHtml5FsFilesystem::AddFile(const Path& path,
168                                    const std::vector<uint8_t>& contents,
169                                    FakeHtml5FsNode** out_node) {
170  NodeMap::iterator iter = node_map_.find(path);
171  if (iter != node_map_.end()) {
172    if (out_node)
173      *out_node = NULL;
174    return false;
175  }
176
177  PP_FileInfo info;
178  info.size = contents.size();
179  info.type = PP_FILETYPE_REGULAR;
180  info.system_type = filesystem_type_;
181  info.creation_time = 0;
182  info.last_access_time = 0;
183  info.last_modified_time = 0;
184
185  FakeHtml5FsNode node(info, contents);
186  std::pair<NodeMap::iterator, bool> result =
187      node_map_.insert(NodeMap::value_type(path, node));
188
189  EXPECT_EQ(true, result.second);
190  if (out_node)
191    *out_node = &result.first->second;
192  return true;
193}
194
195bool FakeHtml5FsFilesystem::AddDirectory(const Path& path,
196                                         FakeHtml5FsNode** out_node) {
197  NodeMap::iterator iter = node_map_.find(path);
198  if (iter != node_map_.end()) {
199    if (out_node)
200      *out_node = NULL;
201    return false;
202  }
203
204  PP_FileInfo info;
205  info.size = 0;
206  info.type = PP_FILETYPE_DIRECTORY;
207  info.system_type = filesystem_type_;
208  info.creation_time = 0;
209  info.last_access_time = 0;
210  info.last_modified_time = 0;
211
212  FakeHtml5FsNode node(info);
213  std::pair<NodeMap::iterator, bool> result =
214      node_map_.insert(NodeMap::value_type(path, node));
215
216  EXPECT_EQ(true, result.second);
217  if (out_node)
218    *out_node = &result.first->second;
219  return true;
220}
221
222bool FakeHtml5FsFilesystem::RemoveNode(const Path& path) {
223  return node_map_.erase(path) >= 1;
224}
225
226FakeHtml5FsNode* FakeHtml5FsFilesystem::GetNode(const Path& path) {
227  NodeMap::iterator iter = node_map_.find(path);
228  if (iter == node_map_.end())
229    return NULL;
230  return &iter->second;
231}
232
233bool FakeHtml5FsFilesystem::GetDirectoryEntries(
234    const Path& path,
235    DirectoryEntries* out_dir_entries) const {
236  out_dir_entries->clear();
237
238  NodeMap::const_iterator iter = node_map_.find(path);
239  if (iter == node_map_.end())
240    return false;
241
242  const FakeHtml5FsNode& dir_node = iter->second;
243  if (!dir_node.IsDirectory())
244    return false;
245
246  for (NodeMap::const_iterator iter = node_map_.begin();
247       iter != node_map_.end();
248       ++iter) {
249    const Path& node_path = iter->first;
250    if (node_path.find(path) == std::string::npos)
251      continue;
252
253    // A node is not a child of itself.
254    if (&iter->second == &dir_node)
255      continue;
256
257    // Only consider children, not descendants. If we find a forward slash, then
258    // the node must be in a subdirectory.
259    if (node_path.find('/', path.size() + 1) != std::string::npos)
260      continue;
261
262    // The directory entry names do not include the path.
263    Path entry_path = node_path;
264    size_t last_slash = node_path.rfind('/');
265    if (last_slash != std::string::npos)
266      entry_path.erase(0, last_slash + 1);
267
268    DirectoryEntry entry;
269    entry.path = entry_path;
270    entry.node = &iter->second;
271    out_dir_entries->push_back(entry);
272  }
273
274  return true;
275}
276
277// static
278FakeHtml5FsFilesystem::Path FakeHtml5FsFilesystem::GetParentPath(
279    const Path& path) {
280  size_t last_slash = path.rfind('/');
281  if (last_slash == 0)
282    return "/";
283
284  EXPECT_EQ(std::string::npos, last_slash);
285  return path.substr(0, last_slash);
286}
287
288FakeFileIoInterface::FakeFileIoInterface(FakeCoreInterface* core_interface)
289    : core_interface_(core_interface) {}
290
291PP_Resource FakeFileIoInterface::Create(PP_Resource) {
292  return CREATE_RESOURCE(core_interface_->resource_manager(),
293                         FakeFileIoResource,
294                         new FakeFileIoResource);
295}
296
297int32_t FakeFileIoInterface::Open(PP_Resource file_io,
298                                  PP_Resource file_ref,
299                                  int32_t open_flags,
300                                  PP_CompletionCallback callback) {
301  FakeFileIoResource* file_io_resource =
302      core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io);
303  if (file_io_resource == NULL)
304    return PP_ERROR_BADRESOURCE;
305
306  bool flag_write = !!(open_flags & PP_FILEOPENFLAG_WRITE);
307  bool flag_create = !!(open_flags & PP_FILEOPENFLAG_CREATE);
308  bool flag_truncate = !!(open_flags & PP_FILEOPENFLAG_TRUNCATE);
309  bool flag_exclusive = !!(open_flags & PP_FILEOPENFLAG_EXCLUSIVE);
310  bool flag_append = !!(open_flags & PP_FILEOPENFLAG_APPEND);
311
312  if ((flag_append && flag_write) || (flag_truncate && !flag_write))
313    return PP_ERROR_BADARGUMENT;
314
315  FakeFileRefResource* file_ref_resource =
316      core_interface_->resource_manager()->Get<FakeFileRefResource>(file_ref);
317  if (file_ref_resource == NULL)
318    return PP_ERROR_BADRESOURCE;
319
320  const FakeHtml5FsFilesystem::Path& path = file_ref_resource->path;
321  FakeHtml5FsFilesystem* filesystem = file_ref_resource->filesystem;
322  FakeHtml5FsNode* node = filesystem->GetNode(path);
323  bool node_exists = node != NULL;
324
325  if (!node_exists) {
326    if (!flag_create)
327      return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
328
329    bool result = filesystem->AddEmptyFile(path, &node);
330    EXPECT_EQ(true, result);
331  } else {
332    if (flag_create && flag_exclusive)
333      return RunCompletionCallback(&callback, PP_ERROR_FILEEXISTS);
334  }
335
336  file_io_resource->node = node;
337  file_io_resource->open_flags = open_flags;
338
339  if (flag_truncate)
340    return RunCompletionCallback(&callback, node->SetLength(0));
341
342  return RunCompletionCallback(&callback, PP_OK);
343}
344
345int32_t FakeFileIoInterface::Query(PP_Resource file_io,
346                                   PP_FileInfo* info,
347                                   PP_CompletionCallback callback) {
348  FakeFileIoResource* file_io_resource =
349      core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io);
350  if (file_io_resource == NULL)
351    return PP_ERROR_BADRESOURCE;
352
353  if (!file_io_resource->node)
354    return RunCompletionCallback(&callback, PP_ERROR_FAILED);
355
356  file_io_resource->node->GetInfo(info);
357  return RunCompletionCallback(&callback, PP_OK);
358}
359
360int32_t FakeFileIoInterface::Read(PP_Resource file_io,
361                                  int64_t offset,
362                                  char* buffer,
363                                  int32_t bytes_to_read,
364                                  PP_CompletionCallback callback) {
365  FakeFileIoResource* file_io_resource =
366      core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io);
367  if (file_io_resource == NULL)
368    return PP_ERROR_BADRESOURCE;
369
370  if (bytes_to_read < 0)
371    return RunCompletionCallback(&callback, PP_ERROR_FAILED);
372
373  if ((file_io_resource->open_flags & PP_FILEOPENFLAG_READ) !=
374      PP_FILEOPENFLAG_READ) {
375    return RunCompletionCallback(&callback, PP_ERROR_NOACCESS);
376  }
377
378  if (!file_io_resource->node)
379    return RunCompletionCallback(&callback, PP_ERROR_FAILED);
380
381  int32_t result = file_io_resource->node->Read(offset, buffer, bytes_to_read);
382  return RunCompletionCallback(&callback, result);
383}
384
385int32_t FakeFileIoInterface::Write(PP_Resource file_io,
386                                   int64_t offset,
387                                   const char* buffer,
388                                   int32_t bytes_to_write,
389                                   PP_CompletionCallback callback) {
390  FakeFileIoResource* file_io_resource =
391      core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io);
392  if (file_io_resource == NULL)
393    return PP_ERROR_BADRESOURCE;
394
395  if ((file_io_resource->open_flags & PP_FILEOPENFLAG_WRITE) !=
396      PP_FILEOPENFLAG_WRITE) {
397    return RunCompletionCallback(&callback, PP_ERROR_NOACCESS);
398  }
399
400  if (!file_io_resource->node)
401    return RunCompletionCallback(&callback, PP_ERROR_FAILED);
402
403  int32_t result;
404  if ((file_io_resource->open_flags & PP_FILEOPENFLAG_APPEND) ==
405      PP_FILEOPENFLAG_APPEND) {
406    result = file_io_resource->node->Append(buffer, bytes_to_write);
407  } else {
408    result = file_io_resource->node->Write(offset, buffer, bytes_to_write);
409  }
410
411  return RunCompletionCallback(&callback, result);
412}
413
414int32_t FakeFileIoInterface::SetLength(PP_Resource file_io,
415                                       int64_t length,
416                                       PP_CompletionCallback callback) {
417  FakeFileIoResource* file_io_resource =
418      core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io);
419  if (file_io_resource == NULL)
420    return PP_ERROR_BADRESOURCE;
421
422  if ((file_io_resource->open_flags & PP_FILEOPENFLAG_WRITE) !=
423      PP_FILEOPENFLAG_WRITE) {
424    return RunCompletionCallback(&callback, PP_ERROR_NOACCESS);
425  }
426
427  if (!file_io_resource->node)
428    return RunCompletionCallback(&callback, PP_ERROR_FAILED);
429
430  int32_t result = file_io_resource->node->SetLength(length);
431  return RunCompletionCallback(&callback, result);
432}
433
434int32_t FakeFileIoInterface::Flush(PP_Resource file_io,
435                                   PP_CompletionCallback callback) {
436  FakeFileIoResource* file_io_resource =
437      core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io);
438  if (file_io_resource == NULL)
439    return PP_ERROR_BADRESOURCE;
440
441  if (!file_io_resource->node)
442    return RunCompletionCallback(&callback, PP_ERROR_FAILED);
443
444  return RunCompletionCallback(&callback, PP_OK);
445}
446
447void FakeFileIoInterface::Close(PP_Resource file_io) {
448  FakeFileIoResource* file_io_resource =
449      core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io);
450  if (file_io_resource == NULL)
451    return;
452
453  file_io_resource->node = NULL;
454  file_io_resource->open_flags = 0;
455}
456
457FakeFileRefInterface::FakeFileRefInterface(FakeCoreInterface* core_interface,
458                                           FakeVarInterface* var_interface)
459    : core_interface_(core_interface), var_interface_(var_interface) {}
460
461PP_Resource FakeFileRefInterface::Create(PP_Resource file_system,
462                                         const char* path) {
463  FakeFileSystemResource* file_system_resource =
464      core_interface_->resource_manager()->Get<FakeFileSystemResource>(
465          file_system);
466  if (file_system_resource == NULL)
467    return PP_ERROR_BADRESOURCE;
468
469  if (!file_system_resource->opened)
470    return PP_ERROR_FAILED;
471
472  if (path == NULL)
473    return PP_ERROR_FAILED;
474
475  size_t path_len = strlen(path);
476  if (path_len == 0)
477    return PP_ERROR_FAILED;
478
479  FakeFileRefResource* file_ref_resource = new FakeFileRefResource;
480  file_ref_resource->filesystem = file_system_resource->filesystem;
481  file_ref_resource->path = path;
482
483  // Remove a trailing slash from the path, unless it is the root path.
484  if (path_len > 1 && file_ref_resource->path[path_len - 1] == '/')
485    file_ref_resource->path.erase(path_len - 1);
486
487  return CREATE_RESOURCE(core_interface_->resource_manager(),
488                         FakeFileRefResource,
489                         file_ref_resource);
490}
491
492PP_Var FakeFileRefInterface::GetName(PP_Resource file_ref) {
493  FakeFileRefResource* file_ref_resource =
494      core_interface_->resource_manager()->Get<FakeFileRefResource>(file_ref);
495  if (file_ref_resource == NULL)
496    return PP_MakeUndefined();
497
498  return var_interface_->VarFromUtf8(file_ref_resource->path.c_str(),
499                                     file_ref_resource->path.size());
500}
501
502int32_t FakeFileRefInterface::MakeDirectory(PP_Resource directory_ref,
503                                            PP_Bool make_parents,
504                                            PP_CompletionCallback callback) {
505  FakeFileRefResource* directory_ref_resource =
506      core_interface_->resource_manager()->Get<FakeFileRefResource>(
507          directory_ref);
508  if (directory_ref_resource == NULL)
509    return PP_ERROR_BADRESOURCE;
510
511  // TODO(binji): We don't currently use make_parents in nacl_io, so
512  // I won't bother handling it yet.
513  if (make_parents == PP_TRUE)
514    return PP_ERROR_FAILED;
515
516  FakeHtml5FsFilesystem* filesystem = directory_ref_resource->filesystem;
517  FakeHtml5FsFilesystem::Path path = directory_ref_resource->path;
518
519  // Pepper returns PP_ERROR_NOACCESS when trying to create the root directory,
520  // not PP_ERROR_FILEEXISTS, as you might expect.
521  if (path == "/")
522    return RunCompletionCallback(&callback, PP_ERROR_NOACCESS);
523
524  FakeHtml5FsNode* node = filesystem->GetNode(path);
525  if (node != NULL)
526    return RunCompletionCallback(&callback, PP_ERROR_FILEEXISTS);
527
528  FakeHtml5FsFilesystem::Path parent_path = filesystem->GetParentPath(path);
529  FakeHtml5FsNode* parent_node = filesystem->GetNode(parent_path);
530  if (parent_node == NULL)
531    return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
532
533  if (!parent_node->IsDirectory())
534    return RunCompletionCallback(&callback, PP_ERROR_FAILED);
535
536  bool result = filesystem->AddDirectory(directory_ref_resource->path, NULL);
537  EXPECT_EQ(true, result);
538  return RunCompletionCallback(&callback, PP_OK);
539}
540
541int32_t FakeFileRefInterface::Delete(PP_Resource file_ref,
542                                     PP_CompletionCallback callback) {
543  FakeFileRefResource* file_ref_resource =
544      core_interface_->resource_manager()->Get<FakeFileRefResource>(file_ref);
545  if (file_ref_resource == NULL)
546    return PP_ERROR_BADRESOURCE;
547
548  FakeHtml5FsFilesystem* filesystem = file_ref_resource->filesystem;
549  FakeHtml5FsFilesystem::Path path = file_ref_resource->path;
550  FakeHtml5FsNode* node = filesystem->GetNode(path);
551  if (node == NULL)
552    return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
553
554  filesystem->RemoveNode(path);
555  return RunCompletionCallback(&callback, PP_OK);
556}
557
558int32_t FakeFileRefInterface::Query(PP_Resource file_ref,
559                                    PP_FileInfo* info,
560                                    PP_CompletionCallback callback) {
561  FakeFileRefResource* file_ref_resource =
562      core_interface_->resource_manager()->Get<FakeFileRefResource>(file_ref);
563  if (file_ref_resource == NULL)
564    return PP_ERROR_BADRESOURCE;
565
566  FakeHtml5FsFilesystem* filesystem = file_ref_resource->filesystem;
567  FakeHtml5FsFilesystem::Path path = file_ref_resource->path;
568  FakeHtml5FsNode* node = filesystem->GetNode(path);
569  if (node == NULL)
570    return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
571
572  node->GetInfo(info);
573  return RunCompletionCallback(&callback, PP_OK);
574}
575
576int32_t FakeFileRefInterface::ReadDirectoryEntries(
577    PP_Resource directory_ref,
578    const PP_ArrayOutput& output,
579    PP_CompletionCallback callback) {
580  FakeFileRefResource* directory_ref_resource =
581      core_interface_->resource_manager()->Get<FakeFileRefResource>(
582          directory_ref);
583  if (directory_ref_resource == NULL)
584    return PP_ERROR_BADRESOURCE;
585
586  FakeHtml5FsFilesystem* filesystem = directory_ref_resource->filesystem;
587  FakeHtml5FsFilesystem::Path path = directory_ref_resource->path;
588  FakeHtml5FsNode* node = filesystem->GetNode(path);
589  if (node == NULL)
590    return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
591
592  if (!node->IsDirectory())
593    return RunCompletionCallback(&callback, PP_ERROR_FAILED);
594
595  FakeHtml5FsFilesystem::DirectoryEntries fake_dir_entries;
596  filesystem->GetDirectoryEntries(path, &fake_dir_entries);
597
598  uint32_t element_count = fake_dir_entries.size();
599  uint32_t element_size = sizeof(fake_dir_entries[0]);
600  void* data_buffer =
601      (*output.GetDataBuffer)(output.user_data, element_count, element_size);
602
603  if (data_buffer == NULL)
604    return RunCompletionCallback(&callback, PP_ERROR_FAILED);
605
606  PP_DirectoryEntry* dir_entries = static_cast<PP_DirectoryEntry*>(data_buffer);
607  for (uint32_t i = 0; i < element_count; ++i) {
608    const FakeHtml5FsFilesystem::DirectoryEntry& fake_dir_entry =
609        fake_dir_entries[i];
610
611    FakeFileRefResource* file_ref_resource = new FakeFileRefResource;
612    file_ref_resource->filesystem = directory_ref_resource->filesystem;
613    file_ref_resource->path = fake_dir_entry.path;
614    PP_Resource file_ref = CREATE_RESOURCE(core_interface_->resource_manager(),
615                                           FakeFileRefResource,
616                                           file_ref_resource);
617
618    dir_entries[i].file_ref = file_ref;
619    dir_entries[i].file_type = fake_dir_entry.node->file_type();
620  }
621
622  return RunCompletionCallback(&callback, PP_OK);
623}
624
625int32_t FakeFileRefInterface::Rename(PP_Resource file_ref,
626                                     PP_Resource new_file_ref,
627                                     PP_CompletionCallback callback) {
628  FakeFileRefResource* file_ref_resource =
629      core_interface_->resource_manager()->Get<FakeFileRefResource>(file_ref);
630  if (file_ref_resource == NULL)
631    return PP_ERROR_BADRESOURCE;
632
633  FakeFileRefResource* new_file_ref_resource =
634      core_interface_->resource_manager()->Get<FakeFileRefResource>(
635          new_file_ref);
636  if (new_file_ref_resource == NULL)
637    return PP_ERROR_BADRESOURCE;
638
639  FakeHtml5FsFilesystem* filesystem = file_ref_resource->filesystem;
640  FakeHtml5FsFilesystem::Path path = file_ref_resource->path;
641  FakeHtml5FsFilesystem::Path newpath = new_file_ref_resource->path;
642  FakeHtml5FsNode* node = filesystem->GetNode(path);
643  if (node == NULL)
644    return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
645  // FakeFileRefResource does not support directory rename.
646  if (!node->IsRegular())
647    return RunCompletionCallback(&callback, PP_ERROR_NOTAFILE);
648
649  // Remove the destination if it exists.
650  filesystem->RemoveNode(newpath);
651  const std::vector<uint8_t> contents = node->contents();
652  EXPECT_TRUE(filesystem->AddFile(newpath, contents, NULL));
653  EXPECT_TRUE(filesystem->RemoveNode(path));
654  return RunCompletionCallback(&callback, PP_OK);
655}
656
657FakeFileSystemInterface::FakeFileSystemInterface(
658    FakeCoreInterface* core_interface)
659    : core_interface_(core_interface) {}
660
661PP_Bool FakeFileSystemInterface::IsFileSystem(PP_Resource resource) {
662  bool not_found_ok = true;
663  FakeFileSystemResource* file_system_resource =
664      core_interface_->resource_manager()->Get<FakeFileSystemResource>(
665          resource, not_found_ok);
666  return file_system_resource != NULL ? PP_TRUE : PP_FALSE;
667}
668
669PP_Resource FakeFileSystemInterface::Create(PP_Instance instance,
670                                            PP_FileSystemType filesystem_type) {
671  FakeInstanceResource* instance_resource =
672      core_interface_->resource_manager()->Get<FakeInstanceResource>(instance);
673  if (instance_resource == NULL)
674    return PP_ERROR_BADRESOURCE;
675
676  FakeFileSystemResource* file_system_resource = new FakeFileSystemResource;
677  file_system_resource->filesystem = new FakeHtml5FsFilesystem(
678      *instance_resource->filesystem_template, filesystem_type);
679
680  return CREATE_RESOURCE(core_interface_->resource_manager(),
681                         FakeFileSystemResource,
682                         file_system_resource);
683}
684
685int32_t FakeFileSystemInterface::Open(PP_Resource file_system,
686                                      int64_t expected_size,
687                                      PP_CompletionCallback callback) {
688  FakeFileSystemResource* file_system_resource =
689      core_interface_->resource_manager()->Get<FakeFileSystemResource>(
690          file_system);
691  if (file_system_resource == NULL)
692    return PP_ERROR_BADRESOURCE;
693
694  file_system_resource->opened = true;
695  return RunCompletionCallback(&callback, PP_OK);
696}
697
698FakePepperInterfaceHtml5Fs::FakePepperInterfaceHtml5Fs()
699    : core_interface_(&resource_manager_),
700      var_interface_(&var_manager_),
701      file_system_interface_(&core_interface_),
702      file_ref_interface_(&core_interface_, &var_interface_),
703      file_io_interface_(&core_interface_) {
704  Init();
705}
706
707FakePepperInterfaceHtml5Fs::FakePepperInterfaceHtml5Fs(
708    const FakeHtml5FsFilesystem& filesystem)
709    : core_interface_(&resource_manager_),
710      var_interface_(&var_manager_),
711      filesystem_template_(filesystem),
712      file_system_interface_(&core_interface_),
713      file_ref_interface_(&core_interface_, &var_interface_),
714      file_io_interface_(&core_interface_),
715      instance_(0) {
716  Init();
717}
718
719void FakePepperInterfaceHtml5Fs::Init() {
720  FakeInstanceResource* instance_resource = new FakeInstanceResource;
721  instance_resource->filesystem_template = &filesystem_template_;
722
723  instance_ = CREATE_RESOURCE(core_interface_.resource_manager(),
724                              FakeInstanceResource,
725                              instance_resource);
726}
727
728FakePepperInterfaceHtml5Fs::~FakePepperInterfaceHtml5Fs() {
729  core_interface_.ReleaseResource(instance_);
730}
731
732nacl_io::CoreInterface* FakePepperInterfaceHtml5Fs::GetCoreInterface() {
733  return &core_interface_;
734}
735
736nacl_io::FileSystemInterface*
737FakePepperInterfaceHtml5Fs::GetFileSystemInterface() {
738  return &file_system_interface_;
739}
740
741nacl_io::FileRefInterface* FakePepperInterfaceHtml5Fs::GetFileRefInterface() {
742  return &file_ref_interface_;
743}
744
745nacl_io::FileIoInterface* FakePepperInterfaceHtml5Fs::GetFileIoInterface() {
746  return &file_io_interface_;
747}
748
749nacl_io::VarInterface* FakePepperInterfaceHtml5Fs::GetVarInterface() {
750  return &var_interface_;
751}
752