Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / libraries / nacl_io / html5fs / html5_fs_node.cc
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/html5fs/html5_fs.h"
20 #include "nacl_io/kernel_handle.h"
21 #include "nacl_io/osdirent.h"
22 #include "nacl_io/pepper_interface.h"
23 #include "sdk_util/auto_lock.h"
24
25 namespace nacl_io {
26
27 namespace {
28
29 struct OutputBuffer {
30   void* data;
31   int element_count;
32 };
33
34 void* GetOutputBuffer(void* user_data, uint32_t count, uint32_t size) {
35   OutputBuffer* output = static_cast<OutputBuffer*>(user_data);
36   output->element_count = count;
37   if (count) {
38     output->data = malloc(count * size);
39     if (!output->data)
40       output->element_count = 0;
41   } else {
42     output->data = NULL;
43   }
44   return output->data;
45 }
46
47 int32_t OpenFlagsToPPAPIOpenFlags(int open_flags) {
48   int32_t ppapi_flags = 0;
49
50   switch (open_flags & 3) {
51     default:
52     case O_RDONLY:
53       ppapi_flags = PP_FILEOPENFLAG_READ;
54       break;
55     case O_WRONLY:
56       ppapi_flags = PP_FILEOPENFLAG_WRITE;
57       break;
58     case O_RDWR:
59       ppapi_flags = PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE;
60       break;
61   }
62
63   if (open_flags & O_CREAT)
64     ppapi_flags |= PP_FILEOPENFLAG_CREATE;
65   if (open_flags & O_TRUNC)
66     ppapi_flags |= PP_FILEOPENFLAG_TRUNCATE;
67   if (open_flags & O_EXCL)
68     ppapi_flags |= PP_FILEOPENFLAG_EXCLUSIVE;
69
70   return ppapi_flags;
71 }
72
73 }  // namespace
74
75 Error Html5FsNode::FSync() {
76   // Cannot call Flush on a directory; simply do nothing.
77   if (IsaDir())
78     return 0;
79
80   int32_t result =
81       file_io_iface_->Flush(fileio_resource_, PP_BlockUntilComplete());
82   if (result != PP_OK)
83     return PPErrorToErrno(result);
84   return 0;
85 }
86
87 Error Html5FsNode::GetDents(size_t offs,
88                             struct dirent* pdir,
89                             size_t size,
90                             int* out_bytes) {
91   *out_bytes = 0;
92
93   // If this is not a directory, fail
94   if (!IsaDir())
95     return ENOTDIR;
96
97   // TODO(binji): Better handling of ino numbers.
98   const ino_t kCurDirIno = -1;
99   const ino_t kParentDirIno = -2;
100   GetDentsHelper helper(kCurDirIno, kParentDirIno);
101
102   OutputBuffer output_buf = {NULL, 0};
103   PP_ArrayOutput output = {&GetOutputBuffer, &output_buf};
104   int32_t result = file_ref_iface_->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 = file_ref_iface_->GetName(entries[i].file_ref);
113
114     // Release the file reference.
115     filesystem_->ppapi()->ReleaseResource(entries[i].file_ref);
116
117     if (file_name_var.type != PP_VARTYPE_STRING)
118       continue;
119
120     uint32_t file_name_length;
121     const char* file_name =
122         var_iface_->VarToUtf8(file_name_var, &file_name_length);
123
124     if (file_name) {
125       file_name_length =
126           std::min(static_cast<size_t>(file_name_length),
127                    MEMBER_SIZE(dirent, d_name) - 1);  // -1 for NULL.
128
129       // The INO is based on the running hash of fully qualified path, so
130       // a childs INO must be the parent directories hash, plus '/', plus
131       // the filename.
132       ino_t child_ino = Html5Fs::HashPathSegment(stat_.st_ino, file_name,
133                                                  file_name_length);
134
135       helper.AddDirent(child_ino, file_name, file_name_length);
136     }
137
138     var_iface_->Release(file_name_var);
139   }
140
141   // Release the output buffer.
142   free(output_buf.data);
143
144   return helper.GetDents(offs, pdir, size, out_bytes);
145 }
146
147 Error Html5FsNode::GetStat(struct stat* stat) {
148   AUTO_LOCK(node_lock_);
149
150   PP_FileInfo info;
151   int32_t result =
152       file_ref_iface_->Query(fileref_resource_, &info, PP_BlockUntilComplete());
153   if (result != PP_OK)
154     return PPErrorToErrno(result);
155
156   // Fill in known info here.
157   memcpy(stat, &stat_, sizeof(stat_));
158
159   // Fill in the additional info from ppapi.
160   switch (info.type) {
161     case PP_FILETYPE_REGULAR:
162       stat->st_mode |= S_IFREG;
163       break;
164     case PP_FILETYPE_DIRECTORY:
165       stat->st_mode |= S_IFDIR;
166       break;
167     case PP_FILETYPE_OTHER:
168     default:
169       break;
170   }
171   stat->st_size = static_cast<off_t>(info.size);
172   stat->st_atime = info.last_access_time;
173   stat->st_mtime = info.last_modified_time;
174   stat->st_ctime = info.creation_time;
175
176   return 0;
177 }
178
179 Error Html5FsNode::Read(const HandleAttr& attr,
180                         void* buf,
181                         size_t count,
182                         int* out_bytes) {
183   *out_bytes = 0;
184
185   if (IsaDir())
186     return EISDIR;
187
188   int32_t result = file_io_iface_->Read(fileio_resource_,
189                                         attr.offs,
190                                         static_cast<char*>(buf),
191                                         static_cast<int32_t>(count),
192                                         PP_BlockUntilComplete());
193   if (result < 0)
194     return PPErrorToErrno(result);
195
196   *out_bytes = result;
197   return 0;
198 }
199
200 Error Html5FsNode::FTruncate(off_t size) {
201   if (IsaDir())
202     return EISDIR;
203
204   int32_t result = file_io_iface_->SetLength(
205       fileio_resource_, size, PP_BlockUntilComplete());
206   if (result != PP_OK)
207     return PPErrorToErrno(result);
208   return 0;
209 }
210
211 Error Html5FsNode::Write(const HandleAttr& attr,
212                          const void* buf,
213                          size_t count,
214                          int* out_bytes) {
215   *out_bytes = 0;
216
217   if (IsaDir())
218     return EISDIR;
219
220   int32_t result = file_io_iface_->Write(fileio_resource_,
221                                          attr.offs,
222                                          static_cast<const char*>(buf),
223                                          static_cast<int32_t>(count),
224                                          PP_BlockUntilComplete());
225   if (result < 0)
226     return PPErrorToErrno(result);
227
228   *out_bytes = result;
229   return 0;
230 }
231
232 int Html5FsNode::GetType() {
233   return fileio_resource_ ? S_IFREG : S_IFDIR;
234 }
235
236 Error Html5FsNode::GetSize(off_t* out_size) {
237   *out_size = 0;
238
239   if (IsaDir())
240     return 0;
241
242   AUTO_LOCK(node_lock_);
243
244   PP_FileInfo info;
245   int32_t result =
246       file_io_iface_->Query(fileio_resource_, &info, PP_BlockUntilComplete());
247   if (result != PP_OK)
248     return PPErrorToErrno(result);
249
250   *out_size = info.size;
251   return 0;
252 }
253
254 Html5FsNode::Html5FsNode(Filesystem* filesystem, PP_Resource fileref_resource)
255     : Node(filesystem),
256       fileref_resource_(fileref_resource),
257       fileio_resource_(0) {
258 }
259
260 Error Html5FsNode::Init(int open_flags) {
261   Error error = Node::Init(open_flags);
262   if (error)
263     return error;
264
265   file_io_iface_ = filesystem_->ppapi()->GetFileIoInterface();
266   file_ref_iface_ = filesystem_->ppapi()->GetFileRefInterface();
267   var_iface_ = filesystem_->ppapi()->GetVarInterface();
268
269   if (!(file_io_iface_ && file_ref_iface_ && var_iface_)) {
270     LOG_ERROR("Got NULL interface(s): %s%s%s",
271               file_ref_iface_ ? "" : "FileRef",
272               file_io_iface_ ? "" : "FileIo ",
273               var_iface_ ? "" : "Var ");
274     return EIO;
275   }
276
277   // Set all files and directories to RWX.
278   SetMode(S_IWALL | S_IRALL | S_IXALL);
279
280   // First query the FileRef to see if it is a file or directory.
281   PP_FileInfo file_info;
282   int32_t query_result = file_ref_iface_->Query(
283       fileref_resource_, &file_info, PP_BlockUntilComplete());
284   // If this is a directory, do not get a FileIO.
285   if (query_result == PP_OK && file_info.type == PP_FILETYPE_DIRECTORY) {
286     return 0;
287   }
288
289   fileio_resource_ =
290       file_io_iface_->Create(filesystem_->ppapi()->GetInstance());
291   if (!fileio_resource_) {
292     LOG_ERROR("Couldn't create FileIo resource.");
293     return EIO;
294   }
295
296   int32_t open_result =
297       file_io_iface_->Open(fileio_resource_,
298                            fileref_resource_,
299                            OpenFlagsToPPAPIOpenFlags(open_flags),
300                            PP_BlockUntilComplete());
301   if (open_result != PP_OK)
302     return PPErrorToErrno(open_result);
303   return 0;
304 }
305
306 void Html5FsNode::Destroy() {
307   FSync();
308
309   if (fileio_resource_) {
310     file_io_iface_->Close(fileio_resource_);
311     filesystem_->ppapi()->ReleaseResource(fileio_resource_);
312   }
313
314   filesystem_->ppapi()->ReleaseResource(fileref_resource_);
315   fileio_resource_ = 0;
316   fileref_resource_ = 0;
317   Node::Destroy();
318 }
319
320 }  // namespace nacl_io