• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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 "nacl_io/html5fs/html5_fs_node.h"
6 
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <ppapi/c/pp_completion_callback.h>
10 #include <ppapi/c/pp_directory_entry.h>
11 #include <ppapi/c/pp_errors.h>
12 #include <ppapi/c/pp_file_info.h>
13 #include <ppapi/c/ppb_file_io.h>
14 #include <string.h>
15 #include <vector>
16 
17 #include "nacl_io/filesystem.h"
18 #include "nacl_io/getdents_helper.h"
19 #include "nacl_io/kernel_handle.h"
20 #include "nacl_io/osdirent.h"
21 #include "nacl_io/pepper_interface.h"
22 #include "sdk_util/auto_lock.h"
23 
24 namespace nacl_io {
25 
26 namespace {
27 
28 struct OutputBuffer {
29   void* data;
30   int element_count;
31 };
32 
GetOutputBuffer(void * user_data,uint32_t count,uint32_t size)33 void* GetOutputBuffer(void* user_data, uint32_t count, uint32_t size) {
34   OutputBuffer* output = static_cast<OutputBuffer*>(user_data);
35   output->element_count = count;
36   if (count) {
37     output->data = malloc(count * size);
38     if (!output->data)
39       output->element_count = 0;
40   } else {
41     output->data = NULL;
42   }
43   return output->data;
44 }
45 
OpenFlagsToPPAPIOpenFlags(int open_flags)46 int32_t OpenFlagsToPPAPIOpenFlags(int open_flags) {
47   int32_t ppapi_flags = 0;
48 
49   switch (open_flags & 3) {
50     default:
51     case O_RDONLY:
52       ppapi_flags = PP_FILEOPENFLAG_READ;
53       break;
54     case O_WRONLY:
55       ppapi_flags = PP_FILEOPENFLAG_WRITE;
56       break;
57     case O_RDWR:
58       ppapi_flags = PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE;
59       break;
60   }
61 
62   if (open_flags & O_CREAT)
63     ppapi_flags |= PP_FILEOPENFLAG_CREATE;
64   if (open_flags & O_TRUNC)
65     ppapi_flags |= PP_FILEOPENFLAG_TRUNCATE;
66   if (open_flags & O_EXCL)
67     ppapi_flags |= PP_FILEOPENFLAG_EXCLUSIVE;
68 
69   return ppapi_flags;
70 }
71 
72 }  // namespace
73 
FSync()74 Error Html5FsNode::FSync() {
75   // Cannot call Flush on a directory; simply do nothing.
76   if (IsaDir())
77     return 0;
78 
79   int32_t result = filesystem_->ppapi()->GetFileIoInterface()->Flush(
80       fileio_resource_, PP_BlockUntilComplete());
81   if (result != PP_OK)
82     return PPErrorToErrno(result);
83   return 0;
84 }
85 
GetDents(size_t offs,struct dirent * pdir,size_t size,int * out_bytes)86 Error Html5FsNode::GetDents(size_t offs,
87                             struct dirent* pdir,
88                             size_t size,
89                             int* out_bytes) {
90   *out_bytes = 0;
91 
92   // If this is not a directory, fail
93   if (!IsaDir())
94     return ENOTDIR;
95 
96   // TODO(binji): Better handling of ino numbers.
97   const ino_t kCurDirIno = -1;
98   const ino_t kParentDirIno = -2;
99   GetDentsHelper helper(kCurDirIno, kParentDirIno);
100 
101   OutputBuffer output_buf = {NULL, 0};
102   PP_ArrayOutput output = {&GetOutputBuffer, &output_buf};
103   int32_t result =
104       filesystem_->ppapi()->GetFileRefInterface()->ReadDirectoryEntries(
105           fileref_resource_, output, PP_BlockUntilComplete());
106   if (result != PP_OK)
107     return PPErrorToErrno(result);
108 
109   PP_DirectoryEntry* entries = static_cast<PP_DirectoryEntry*>(output_buf.data);
110 
111   for (int i = 0; i < output_buf.element_count; ++i) {
112     PP_Var file_name_var = filesystem_->ppapi()->GetFileRefInterface()->GetName(
113         entries[i].file_ref);
114 
115     // Release the file reference.
116     filesystem_->ppapi()->ReleaseResource(entries[i].file_ref);
117 
118     if (file_name_var.type != PP_VARTYPE_STRING)
119       continue;
120 
121     uint32_t file_name_length;
122     const char* file_name = filesystem_->ppapi()->GetVarInterface()->VarToUtf8(
123         file_name_var, &file_name_length);
124 
125     if (file_name) {
126       file_name_length =
127           std::min(static_cast<size_t>(file_name_length),
128                    MEMBER_SIZE(dirent, d_name) - 1);  // -1 for NULL.
129 
130       // TODO(binji): Better handling of ino numbers.
131       helper.AddDirent(1, file_name, file_name_length);
132     }
133 
134     filesystem_->ppapi()->GetVarInterface()->Release(file_name_var);
135   }
136 
137   // Release the output buffer.
138   free(output_buf.data);
139 
140   return helper.GetDents(offs, pdir, size, out_bytes);
141 }
142 
GetStat(struct stat * stat)143 Error Html5FsNode::GetStat(struct stat* stat) {
144   AUTO_LOCK(node_lock_);
145 
146   PP_FileInfo info;
147   int32_t result = filesystem_->ppapi()->GetFileRefInterface()->Query(
148       fileref_resource_, &info, PP_BlockUntilComplete());
149   if (result != PP_OK)
150     return PPErrorToErrno(result);
151 
152   // Fill in known info here.
153   memcpy(stat, &stat_, sizeof(stat_));
154 
155   // Fill in the additional info from ppapi.
156   switch (info.type) {
157     case PP_FILETYPE_REGULAR:
158       stat->st_mode |= S_IFREG;
159       break;
160     case PP_FILETYPE_DIRECTORY:
161       stat->st_mode |= S_IFDIR;
162       break;
163     case PP_FILETYPE_OTHER:
164     default:
165       break;
166   }
167   stat->st_size = static_cast<off_t>(info.size);
168   stat->st_atime = info.last_access_time;
169   stat->st_mtime = info.last_modified_time;
170   stat->st_ctime = info.creation_time;
171 
172   return 0;
173 }
174 
Read(const HandleAttr & attr,void * buf,size_t count,int * out_bytes)175 Error Html5FsNode::Read(const HandleAttr& attr,
176                         void* buf,
177                         size_t count,
178                         int* out_bytes) {
179   *out_bytes = 0;
180 
181   if (IsaDir())
182     return EISDIR;
183 
184   int32_t result = filesystem_->ppapi()->GetFileIoInterface()->Read(
185       fileio_resource_,
186       attr.offs,
187       static_cast<char*>(buf),
188       static_cast<int32_t>(count),
189       PP_BlockUntilComplete());
190   if (result < 0)
191     return PPErrorToErrno(result);
192 
193   *out_bytes = result;
194   return 0;
195 }
196 
FTruncate(off_t size)197 Error Html5FsNode::FTruncate(off_t size) {
198   if (IsaDir())
199     return EISDIR;
200 
201   int32_t result = filesystem_->ppapi()->GetFileIoInterface()->SetLength(
202       fileio_resource_, size, PP_BlockUntilComplete());
203   if (result != PP_OK)
204     return PPErrorToErrno(result);
205   return 0;
206 }
207 
Write(const HandleAttr & attr,const void * buf,size_t count,int * out_bytes)208 Error Html5FsNode::Write(const HandleAttr& attr,
209                          const void* buf,
210                          size_t count,
211                          int* out_bytes) {
212   *out_bytes = 0;
213 
214   if (IsaDir())
215     return EISDIR;
216 
217   int32_t result = filesystem_->ppapi()->GetFileIoInterface()->Write(
218       fileio_resource_,
219       attr.offs,
220       static_cast<const char*>(buf),
221       static_cast<int32_t>(count),
222       PP_BlockUntilComplete());
223   if (result < 0)
224     return PPErrorToErrno(result);
225 
226   *out_bytes = result;
227   return 0;
228 }
229 
GetType()230 int Html5FsNode::GetType() {
231   return fileio_resource_ ? S_IFREG : S_IFDIR;
232 }
233 
GetSize(off_t * out_size)234 Error Html5FsNode::GetSize(off_t* out_size) {
235   *out_size = 0;
236 
237   if (IsaDir())
238     return 0;
239 
240   AUTO_LOCK(node_lock_);
241 
242   PP_FileInfo info;
243   int32_t result = filesystem_->ppapi()->GetFileIoInterface()->Query(
244       fileio_resource_, &info, PP_BlockUntilComplete());
245   if (result != PP_OK)
246     return PPErrorToErrno(result);
247 
248   *out_size = info.size;
249   return 0;
250 }
251 
Html5FsNode(Filesystem * filesystem,PP_Resource fileref_resource)252 Html5FsNode::Html5FsNode(Filesystem* filesystem, PP_Resource fileref_resource)
253     : Node(filesystem),
254       fileref_resource_(fileref_resource),
255       fileio_resource_(0) {
256 }
257 
Init(int open_flags)258 Error Html5FsNode::Init(int open_flags) {
259   Error error = Node::Init(open_flags);
260   if (error)
261     return error;
262 
263   // First query the FileRef to see if it is a file or directory.
264   PP_FileInfo file_info;
265   int32_t query_result = filesystem_->ppapi()->GetFileRefInterface()->Query(
266       fileref_resource_, &file_info, PP_BlockUntilComplete());
267   // If this is a directory, do not get a FileIO.
268   if (query_result == PP_OK && file_info.type == PP_FILETYPE_DIRECTORY)
269     return 0;
270 
271   FileIoInterface* file_io = filesystem_->ppapi()->GetFileIoInterface();
272   fileio_resource_ = file_io->Create(filesystem_->ppapi()->GetInstance());
273   if (!fileio_resource_)
274     return ENOSYS;
275 
276   int32_t open_result = file_io->Open(fileio_resource_,
277                                       fileref_resource_,
278                                       OpenFlagsToPPAPIOpenFlags(open_flags),
279                                       PP_BlockUntilComplete());
280   if (open_result != PP_OK)
281     return PPErrorToErrno(open_result);
282   return 0;
283 }
284 
Destroy()285 void Html5FsNode::Destroy() {
286   FSync();
287 
288   if (fileio_resource_) {
289     filesystem_->ppapi()->GetFileIoInterface()->Close(fileio_resource_);
290     filesystem_->ppapi()->ReleaseResource(fileio_resource_);
291   }
292 
293   filesystem_->ppapi()->ReleaseResource(fileref_resource_);
294   fileio_resource_ = 0;
295   fileref_resource_ = 0;
296   Node::Destroy();
297 }
298 
299 }  // namespace nacl_io
300