1 // Copyright 2014 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/jsfs/js_fs_node.h" 6 7 #include <assert.h> 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <limits.h> 11 #include <string.h> 12 13 #include "nacl_io/jsfs/js_fs.h" 14 #include "nacl_io/kernel_handle.h" 15 #include "nacl_io/log.h" 16 #include "nacl_io/osdirent.h" 17 #include "nacl_io/pepper_interface.h" 18 #include "sdk_util/macros.h" 19 20 namespace nacl_io { 21 22 JsFsNode::JsFsNode(Filesystem* filesystem, int32_t fd) 23 : Node(filesystem), 24 ppapi_(filesystem->ppapi()), 25 array_iface_(ppapi_->GetVarArrayInterface()), 26 buffer_iface_(ppapi_->GetVarArrayBufferInterface()), 27 var_iface_(ppapi_->GetVarInterface()), 28 fd_(fd) { 29 } 30 31 void JsFsNode::Destroy() { 32 // TODO(binji): implement 33 } 34 35 bool JsFsNode::SendRequestAndWait(ScopedVar* out_response, 36 const char* format, 37 ...) { 38 va_list args; 39 va_start(args, format); 40 bool result = filesystem()->VSendRequestAndWait(out_response, format, args); 41 va_end(args); 42 return result; 43 } 44 45 int JsFsNode::ScanVar(PP_Var var, const char* format, ...) { 46 va_list args; 47 va_start(args, format); 48 int result = filesystem()->VScanVar(var, format, args); 49 va_end(args); 50 return result; 51 } 52 53 bool JsFsNode::CanOpen(int open_flags) { 54 struct stat statbuf; 55 Error error = GetStat(&statbuf); 56 if (error) 57 return false; 58 59 // GetStat cached the mode in stat_.st_mode. Forward to Node::CanOpen, 60 // which will check this mode against open_flags. 61 return Node::CanOpen(open_flags); 62 } 63 64 Error JsFsNode::GetStat(struct stat* stat) { 65 AUTO_LOCK(node_lock_); 66 67 ScopedVar response(ppapi_); 68 if (!SendRequestAndWait(&response, "%s%d", "cmd", "fstat", "fildes", fd_)) { 69 LOG_ERROR("Failed to send request."); 70 return EINVAL; 71 } 72 73 // TODO(binji): find out the size of bionic stat fields. 74 #if defined(__native_client__) && !defined(__BIONIC__) 75 #if defined(__GLIBC__) 76 const char* format = "%d%lld%d%d%d%d%lld%lld%lld%lld%lld%lld%lld"; 77 #else 78 const char* format = "%d%lld%d%d%d%d%lld%lld%d%d%lld%lld%lld"; 79 #endif 80 #else 81 #define FIELD(x) \ 82 assert(sizeof(stat->x) >= sizeof(int32_t)); \ 83 strcat(format, sizeof(stat->x) == sizeof(int64_t) ? "%lld" : "%d"); 84 85 // For host builds, we'll build up the format string at runtime. 86 char format[100] = "%d"; // First field is "error". 87 FIELD(st_ino); 88 FIELD(st_mode); 89 FIELD(st_nlink); 90 FIELD(st_uid); 91 FIELD(st_gid); 92 FIELD(st_rdev); 93 FIELD(st_size); 94 FIELD(st_blksize); 95 FIELD(st_blocks); 96 FIELD(st_atime); 97 FIELD(st_mtime); 98 FIELD(st_ctime); 99 100 #undef FIELD 101 #endif 102 103 int32_t error; 104 int result = ScanVar(response.pp_var(), 105 format, 106 "error", &error, 107 "st_ino", &stat->st_ino, 108 "st_mode", &stat->st_mode, 109 "st_nlink", &stat->st_nlink, 110 "st_uid", &stat->st_uid, 111 "st_gid", &stat->st_gid, 112 "st_rdev", &stat->st_rdev, 113 "st_size", &stat->st_size, 114 "st_blksize", &stat->st_blksize, 115 "st_blocks", &stat->st_blocks, 116 "st_atime", &stat->st_atime, 117 "st_mtime", &stat->st_mtime, 118 "st_ctime", &stat->st_ctime); 119 120 if (result >= 1 && error) 121 return error; 122 123 if (result != 13) { 124 LOG_ERROR( 125 "Expected \"st_*\" and \"error\" fields in response (should be 13 " 126 "total)."); 127 return EINVAL; 128 } 129 130 stat->st_dev = filesystem()->dev(); 131 132 return 0; 133 } 134 135 Error JsFsNode::GetSize(off_t* out_size) { 136 *out_size = 0; 137 138 struct stat statbuf; 139 Error error = GetStat(&statbuf); 140 if (error) 141 return error; 142 143 *out_size = stat_.st_size; 144 return 0; 145 } 146 147 Error JsFsNode::FSync() { 148 AUTO_LOCK(node_lock_); 149 150 ScopedVar response(ppapi_); 151 if (!SendRequestAndWait(&response, "%s%d", "cmd", "fsync", "fildes", fd_)) { 152 LOG_ERROR("Failed to send request."); 153 return EINVAL; 154 } 155 156 return filesystem()->ErrorFromResponse(response); 157 } 158 159 Error JsFsNode::FTruncate(off_t length) { 160 AUTO_LOCK(node_lock_); 161 162 ScopedVar response(ppapi_); 163 if (!SendRequestAndWait(&response, 164 "%s%d%lld", "cmd", "ftruncate", "fildes", fd_, "length", length)) { 165 LOG_ERROR("Failed to send request."); 166 return EINVAL; 167 } 168 169 return filesystem()->ErrorFromResponse(response); 170 } 171 172 Error JsFsNode::Read(const HandleAttr& attr, 173 void* buf, 174 size_t count, 175 int* out_bytes) { 176 AUTO_LOCK(node_lock_); 177 178 *out_bytes = 0; 179 180 ScopedVar response(ppapi_); 181 if (!SendRequestAndWait(&response, "%s%d%u%lld", 182 "cmd", "pread", 183 "fildes", fd_, 184 "nbyte", count, 185 "offset", attr.offs)) { 186 LOG_ERROR("Failed to send request."); 187 return EINVAL; 188 } 189 190 int32_t error; 191 192 PP_Var buf_var; 193 int result = 194 ScanVar(response.pp_var(), "%d%p", "error", &error, "buf", &buf_var); 195 ScopedVar scoped_buf_var(ppapi_, buf_var); 196 197 if (result >= 1 && error) 198 return error; 199 200 if (result != 2) { 201 LOG_ERROR("Expected \"error\" and \"buf\" fields in response."); 202 return EINVAL; 203 } 204 205 if (buf_var.type != PP_VARTYPE_ARRAY_BUFFER) { 206 LOG_ERROR("Expected \"buf\" to be an ArrayBuffer."); 207 return EINVAL; 208 } 209 210 uint32_t src_buf_len; 211 if (!buffer_iface_->ByteLength(buf_var, &src_buf_len)) { 212 LOG_ERROR("Unable to get byteLength of \"buf\"."); 213 return EINVAL; 214 } 215 216 if (src_buf_len > count) 217 src_buf_len = count; 218 219 void* src_buf = buffer_iface_->Map(buf_var); 220 if (src_buf == NULL) { 221 LOG_ERROR("Unable to map \"buf\"."); 222 return EINVAL; 223 } 224 225 memcpy(buf, src_buf, src_buf_len); 226 *out_bytes = src_buf_len; 227 228 buffer_iface_->Unmap(buf_var); 229 230 return 0; 231 } 232 233 Error JsFsNode::Write(const HandleAttr& attr, 234 const void* buf, 235 size_t count, 236 int* out_bytes) { 237 AUTO_LOCK(node_lock_); 238 239 *out_bytes = 0; 240 241 PP_Var buf_var = buffer_iface_->Create(count); 242 ScopedVar scoped_buf_var(ppapi_, buf_var); 243 244 if (buf_var.type != PP_VARTYPE_ARRAY_BUFFER) { 245 LOG_ERROR("Unable to create \"buf\" var."); 246 return EINVAL; 247 } 248 249 void* dst_buf = buffer_iface_->Map(buf_var); 250 if (dst_buf == NULL) { 251 LOG_ERROR("Unable to map \"buf\"."); 252 return EINVAL; 253 } 254 255 memcpy(dst_buf, buf, count); 256 257 buffer_iface_->Unmap(buf_var); 258 259 ScopedVar response(ppapi_); 260 if (!SendRequestAndWait(&response, "%s%d%p%u%lld", 261 "cmd", "pwrite", 262 "fildes", fd_, 263 "buf", &buf_var, 264 "nbyte", count, 265 "offset", attr.offs)) { 266 LOG_ERROR("Failed to send request."); 267 return EINVAL; 268 } 269 270 int error; 271 uint32_t nwrote; 272 int result = 273 ScanVar(response.pp_var(), "%d%u", "error", &error, "nwrote", &nwrote); 274 275 if (result >= 1 && error) 276 return error; 277 278 if (result != 2) { 279 LOG_ERROR("Expected \"error\" and \"nwrote\" fields in response."); 280 return EINVAL; 281 } 282 283 *out_bytes = nwrote; 284 return 0; 285 } 286 287 Error JsFsNode::GetDents(size_t offs, 288 struct dirent* pdir, 289 size_t count, 290 int* out_bytes) { 291 AUTO_LOCK(node_lock_); 292 293 *out_bytes = 0; 294 295 // Round to the nearest sizeof(dirent) and ask for that. 296 size_t first = offs / sizeof(dirent); 297 size_t last = (offs + count + sizeof(dirent) - 1) / sizeof(dirent); 298 299 ScopedVar response(ppapi_); 300 if (!SendRequestAndWait(&response, "%s%d%u%u", 301 "cmd", "getdents", 302 "fildes", fd_, 303 "offs", first, 304 "count", last - first)) { 305 LOG_ERROR("Failed to send request."); 306 return EINVAL; 307 } 308 309 int error; 310 PP_Var dirents_var; 311 int result = ScanVar( 312 response.pp_var(), "%d%p", "error", &error, "dirents", &dirents_var); 313 314 ScopedVar scoped_dirents_var(ppapi_, dirents_var); 315 316 if (result >= 1 && error) 317 return error; 318 319 if (result != 2) { 320 LOG_ERROR("Expected \"error\" and \"dirents\" fields in response."); 321 return EINVAL; 322 } 323 324 if (dirents_var.type != PP_VARTYPE_ARRAY) { 325 LOG_ERROR("Expected \"dirents\" to be an Array."); 326 return EINVAL; 327 } 328 329 uint32_t dirents_len = array_iface_->GetLength(dirents_var); 330 uint32_t dirents_byte_len = dirents_len * sizeof(dirent); 331 332 // Allocate enough full dirents to copy from. This makes it easier if, for 333 // some reason, we are reading unaligned dirents. 334 dirent* dirents = static_cast<dirent*>(malloc(dirents_byte_len)); 335 336 for (uint32_t i = 0; i < dirents_len; ++i) { 337 PP_Var dirent_var = array_iface_->Get(dirents_var, i); 338 PP_Var d_name_var; 339 result = ScanVar(dirent_var, 340 "%lld%p", 341 "d_ino", &dirents[i].d_ino, 342 "d_name", &d_name_var); 343 ScopedVar scoped_dirent_var(ppapi_, dirent_var); 344 ScopedVar scoped_d_name_var(ppapi_, d_name_var); 345 346 if (result != 2) { 347 LOG_ERROR("Expected dirent[%d] to have \"d_ino\" and \"d_name\".", i); 348 free(dirents); 349 return EINVAL; 350 } 351 352 uint32_t d_name_len; 353 const char* d_name = var_iface_->VarToUtf8(d_name_var, &d_name_len); 354 355 dirents[i].d_off = sizeof(dirent); 356 dirents[i].d_reclen = sizeof(dirent); 357 strncpy(dirents[i].d_name, d_name, sizeof(dirents[i].d_name)); 358 } 359 360 size_t dirents_offs = offs - first * sizeof(dirent); 361 if (dirents_offs + count > dirents_byte_len) 362 count = dirents_byte_len - dirents_offs; 363 364 memcpy(pdir, reinterpret_cast<const char*>(dirents) + dirents_offs, count); 365 *out_bytes = count; 366 367 free(dirents); 368 return 0; 369 } 370 371 } // namespace nacl_io 372