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