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 25namespace nacl_io { 26 27namespace { 28 29struct OutputBuffer { 30 void* data; 31 int element_count; 32}; 33 34void* 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 47int32_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 75Error 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 87Error 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 147Error 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 179Error 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 200Error 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 211Error 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 232int Html5FsNode::GetType() { 233 return fileio_resource_ ? S_IFREG : S_IFDIR; 234} 235 236Error 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 254Html5FsNode::Html5FsNode(Filesystem* filesystem, PP_Resource fileref_resource) 255 : Node(filesystem), 256 fileref_resource_(fileref_resource), 257 fileio_resource_(0) { 258} 259 260Error 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 306void 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 321