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 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 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 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 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 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 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 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 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 230 int Html5FsNode::GetType() { 231 return fileio_resource_ ? S_IFREG : S_IFDIR; 232 } 233 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 252 Html5FsNode::Html5FsNode(Filesystem* filesystem, PP_Resource fileref_resource) 253 : Node(filesystem), 254 fileref_resource_(fileref_resource), 255 fileio_resource_(0) { 256 } 257 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 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