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