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