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 6 #include "handlers.h" 7 8 #include <arpa/inet.h> 9 #include <assert.h> 10 #include <errno.h> 11 #include <netdb.h> 12 #include <netinet/in.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 17 #include <sys/types.h> 18 #include <sys/socket.h> 19 #include <sys/stat.h> 20 21 #include "nacl_io/osdirent.h" 22 23 #include "nacl_io_demo.h" 24 25 #define MAX_OPEN_FILES 10 26 #define MAX_OPEN_DIRS 10 27 28 #if defined(WIN32) 29 #define stat _stat 30 #endif 31 32 /** 33 * A mapping from int -> FILE*, so the JavaScript messages can refer to an open 34 * File. */ 35 static FILE* g_OpenFiles[MAX_OPEN_FILES]; 36 37 /** 38 * A mapping from int -> DIR*, so the JavaScript messages can refer to an open 39 * Directory. */ 40 static void* g_OpenDirs[MAX_OPEN_DIRS]; 41 42 /** 43 * Add |object| to |map| and return the index it was added at. 44 * @param[in] map The map to add the object to. 45 * @param[in] max_map_size The maximum map size. 46 * @param[in] object The object to add to the map. 47 * @return int The index of the added object, or -1 if there is no more space. 48 */ 49 static int AddToMap(void** map, int max_map_size, void* object) { 50 int i; 51 assert(object != NULL); 52 for (i = 0; i < max_map_size; ++i) { 53 if (map[i] == NULL) { 54 map[i] = object; 55 return i; 56 } 57 } 58 59 return -1; 60 } 61 62 /** 63 * Remove an object at index |i| from |map|. 64 * @param[in] map The map to remove from. 65 * @param[in] max_map_size The size of the map. 66 * @param[in] i The index to remove. 67 */ 68 static void RemoveFromMap(void** map, int max_map_size, int i) { 69 assert(i >= 0 && i < max_map_size); 70 map[i] = NULL; 71 } 72 73 /** 74 * Get the object from |map| at index |i|. 75 * @param[in] map The map to access. 76 * @param[in] max_map_size The size of the map. 77 * @param[in] i The index to access. 78 * @return the object at |map|. This will be NULL if there is no object at |i|. 79 */ 80 static void* GetFromMap(void** map, int max_map_size, int i) { 81 assert(i >= 0 && i < max_map_size); 82 return map[i]; 83 } 84 85 /** 86 * Get an object given a string |s| containing the index. 87 * @param[in] map The map to access. 88 * @param[in] max_map_size The size of the map. 89 * @param[in] s The string containing the object index. 90 * @param[out] index The index of the object as an int. 91 * @return The object, or NULL if the index is invalid. 92 */ 93 static void* GetFromIndexString(void** map, 94 int max_map_size, 95 const char* s, 96 int* index) { 97 char* endptr; 98 int result = strtol(s, &endptr, 10); 99 if (endptr != s + strlen(s)) { 100 /* Garbage at the end of the number...? */ 101 return NULL; 102 } 103 104 if (index) 105 *index = result; 106 107 return GetFromMap(map, max_map_size, result); 108 } 109 110 /** 111 * Add the file to the g_OpenFiles map. 112 * @param[in] file The file to add to g_OpenFiles. 113 * @return int The index of the FILE in g_OpenFiles, or -1 if there are too many 114 * open files. */ 115 static int AddFileToMap(FILE* file) { 116 return AddToMap((void**)g_OpenFiles, MAX_OPEN_FILES, file); 117 } 118 119 /** 120 * Remove the file from the g_OpenFiles map. 121 * @param[in] i The index of the file handle to remove. */ 122 static void RemoveFileFromMap(int i) { 123 RemoveFromMap((void**)g_OpenFiles, MAX_OPEN_FILES, i); 124 } 125 126 /** 127 * Get a file, given a string containing the index. 128 * @param[in] s The string containing the file index. 129 * @param[out] file_index The index of this file. 130 * @return The FILE* for this file, or NULL if the index is invalid. 131 */ 132 static FILE* GetFileFromIndexString(const char* s, int* file_index) { 133 return (FILE*)GetFromIndexString( 134 (void**)g_OpenFiles, MAX_OPEN_FILES, s, file_index); 135 } 136 137 /* Win32 doesn't support DIR/opendir/readdir/closedir. */ 138 #if !defined(WIN32) 139 /** 140 * Add the dir to the g_OpenDirs map. 141 * @param[in] dir The dir to add to g_OpenDirs. 142 * @return int The index of the DIR in g_OpenDirs, or -1 if there are too many 143 * open dirs. */ 144 static int AddDirToMap(DIR* dir) { 145 return AddToMap((void**)g_OpenDirs, MAX_OPEN_DIRS, dir); 146 } 147 148 /** 149 * Remove the dir from the g_OpenDirs map. 150 * @param[in] i The index of the dir handle to remove. */ 151 static void RemoveDirFromMap(int i) { 152 RemoveFromMap((void**)g_OpenDirs, MAX_OPEN_DIRS, i); 153 } 154 155 /** 156 * Get a dir, given a string containing the index. 157 * @param[in] s The string containing the dir index. 158 * @param[out] dir_index The index of this dir. 159 * @return The DIR* for this dir, or NULL if the index is invalid. 160 */ 161 static DIR* GetDirFromIndexString(const char* s, int* dir_index) { 162 return (DIR*)GetFromIndexString( 163 (void**)g_OpenDirs, MAX_OPEN_DIRS, s, dir_index); 164 } 165 #endif 166 167 /** 168 * Handle a call to fopen() made by JavaScript. 169 * 170 * fopen expects 2 parameters: 171 * 0: the path of the file to open 172 * 1: the mode string 173 * on success, fopen returns a result in |output| separated by \1: 174 * 0: "fopen" 175 * 1: the filename opened 176 * 2: the file index 177 * on failure, fopen returns an error string in |output|. 178 * 179 * @param[in] num_params The number of params in |params|. 180 * @param[in] params An array of strings, parameters to this function. 181 * @param[out] output A string to write informational function output to. 182 * @return An errorcode; 0 means success, anything else is a failure. */ 183 int HandleFopen(int num_params, char** params, char** output) { 184 FILE* file; 185 int file_index; 186 const char* filename; 187 const char* mode; 188 189 if (num_params != 2) { 190 *output = PrintfToNewString("Error: fopen takes 2 parameters."); 191 return 1; 192 } 193 194 filename = params[0]; 195 mode = params[1]; 196 197 file = fopen(filename, mode); 198 if (!file) { 199 *output = PrintfToNewString("Error: fopen returned a NULL FILE*."); 200 return 2; 201 } 202 203 file_index = AddFileToMap(file); 204 if (file_index == -1) { 205 *output = PrintfToNewString( 206 "Error: Example only allows %d open file handles.", MAX_OPEN_FILES); 207 return 3; 208 } 209 210 *output = PrintfToNewString("fopen\1%s\1%d", filename, file_index); 211 return 0; 212 } 213 214 /** 215 * Handle a call to fwrite() made by JavaScript. 216 * 217 * fwrite expects 2 parameters: 218 * 0: The index of the file (which is mapped to a FILE*) 219 * 1: A string to write to the file 220 * on success, fwrite returns a result in |output| separated by \1: 221 * 0: "fwrite" 222 * 1: the file index 223 * 2: the number of bytes written 224 * on failure, fwrite returns an error string in |output|. 225 * 226 * @param[in] num_params The number of params in |params|. 227 * @param[in] params An array of strings, parameters to this function. 228 * @param[out] output A string to write informational function output to. 229 * @return An errorcode; 0 means success, anything else is a failure. */ 230 int HandleFwrite(int num_params, char** params, char** output) { 231 FILE* file; 232 const char* file_index_string; 233 const char* data; 234 size_t data_len; 235 size_t bytes_written; 236 237 if (num_params != 2) { 238 *output = PrintfToNewString("Error: fwrite takes 2 parameters."); 239 return 1; 240 } 241 242 file_index_string = params[0]; 243 file = GetFileFromIndexString(file_index_string, NULL); 244 data = params[1]; 245 data_len = strlen(data); 246 247 if (!file) { 248 *output = 249 PrintfToNewString("Error: Unknown file handle %s.", file_index_string); 250 return 2; 251 } 252 253 bytes_written = fwrite(data, 1, data_len, file); 254 255 if (ferror(file)) { 256 *output = PrintfToNewString( 257 "Error: Wrote %d bytes, but ferror() returns true.", bytes_written); 258 return 3; 259 } 260 261 *output = 262 PrintfToNewString("fwrite\1%s\1%d", file_index_string, bytes_written); 263 return 0; 264 } 265 266 /** 267 * Handle a call to fread() made by JavaScript. 268 * 269 * fread expects 2 parameters: 270 * 0: The index of the file (which is mapped to a FILE*) 271 * 1: The number of bytes to read from the file. 272 * on success, fread returns a result in |output| separated by \1: 273 * 0: "fread" 274 * 1: the file index 275 * 2: the data read from the file 276 * on failure, fread returns an error string in |output|. 277 * 278 * @param[in] num_params The number of params in |params|. 279 * @param[in] params An array of strings, parameters to this function. 280 * @param[out] output A string to write informational function output to. 281 * @return An errorcode; 0 means success, anything else is a failure. */ 282 int HandleFread(int num_params, char** params, char** output) { 283 FILE* file; 284 const char* file_index_string; 285 char* buffer; 286 size_t data_len; 287 size_t bytes_read; 288 289 if (num_params != 2) { 290 *output = PrintfToNewString("Error: fread takes 2 parameters."); 291 return 1; 292 } 293 294 file_index_string = params[0]; 295 file = GetFileFromIndexString(file_index_string, NULL); 296 data_len = strtol(params[1], NULL, 10); 297 298 if (!file) { 299 *output = 300 PrintfToNewString("Error: Unknown file handle %s.", file_index_string); 301 return 2; 302 } 303 304 buffer = (char*)malloc(data_len + 1); 305 bytes_read = fread(buffer, 1, data_len, file); 306 buffer[bytes_read] = 0; 307 308 if (ferror(file)) { 309 *output = PrintfToNewString( 310 "Error: Read %d bytes, but ferror() returns true.", bytes_read); 311 return 3; 312 } 313 314 *output = PrintfToNewString("fread\1%s\1%s", file_index_string, buffer); 315 free(buffer); 316 return 0; 317 } 318 319 /** 320 * Handle a call to fseek() made by JavaScript. 321 * 322 * fseek expects 3 parameters: 323 * 0: The index of the file (which is mapped to a FILE*) 324 * 1: The offset to seek to 325 * 2: An integer representing the whence parameter of standard fseek. 326 * whence = 0: seek from the beginning of the file 327 * whence = 1: seek from the current file position 328 * whence = 2: seek from the end of the file 329 * on success, fseek returns a result in |output| separated by \1: 330 * 0: "fseek" 331 * 1: the file index 332 * 2: The new file position 333 * on failure, fseek returns an error string in |output|. 334 * 335 * @param[in] num_params The number of params in |params|. 336 * @param[in] params An array of strings, parameters to this function. 337 * @param[out] output A string to write informational function output to. 338 * @return An errorcode; 0 means success, anything else is a failure. */ 339 int HandleFseek(int num_params, char** params, char** output) { 340 FILE* file; 341 const char* file_index_string; 342 long offset; 343 int whence; 344 int result; 345 346 if (num_params != 3) { 347 *output = PrintfToNewString("Error: fseek takes 3 parameters."); 348 return 1; 349 } 350 351 file_index_string = params[0]; 352 file = GetFileFromIndexString(file_index_string, NULL); 353 offset = strtol(params[1], NULL, 10); 354 whence = strtol(params[2], NULL, 10); 355 356 if (!file) { 357 *output = 358 PrintfToNewString("Error: Unknown file handle %s.", file_index_string); 359 return 2; 360 } 361 362 result = fseek(file, offset, whence); 363 if (result) { 364 *output = PrintfToNewString("Error: fseek returned error %d.", result); 365 return 3; 366 } 367 368 offset = ftell(file); 369 if (offset < 0) { 370 *output = PrintfToNewString( 371 "Error: fseek succeeded, but ftell returned error %d.", offset); 372 return 4; 373 } 374 375 *output = PrintfToNewString("fseek\1%s\1%d", file_index_string, offset); 376 return 0; 377 } 378 379 /** 380 * Handle a call to fclose() made by JavaScript. 381 * 382 * fclose expects 1 parameter: 383 * 0: The index of the file (which is mapped to a FILE*) 384 * on success, fclose returns a result in |output| separated by \1: 385 * 0: "fclose" 386 * 1: the file index 387 * on failure, fclose returns an error string in |output|. 388 * 389 * @param[in] num_params The number of params in |params|. 390 * @param[in] params An array of strings, parameters to this function. 391 * @param[out] output A string to write informational function output to. 392 * @return An errorcode; 0 means success, anything else is a failure. */ 393 int HandleFclose(int num_params, char** params, char** output) { 394 FILE* file; 395 int file_index; 396 const char* file_index_string; 397 int result; 398 399 if (num_params != 1) { 400 *output = PrintfToNewString("Error: fclose takes 1 parameters."); 401 return 1; 402 } 403 404 file_index_string = params[0]; 405 file = GetFileFromIndexString(file_index_string, &file_index); 406 if (!file) { 407 *output = 408 PrintfToNewString("Error: Unknown file handle %s.", file_index_string); 409 return 2; 410 } 411 412 result = fclose(file); 413 if (result) { 414 *output = PrintfToNewString("Error: fclose returned error %d.", result); 415 return 3; 416 } 417 418 RemoveFileFromMap(file_index); 419 420 *output = PrintfToNewString("fclose\1%s", file_index_string); 421 return 0; 422 } 423 424 /** 425 * Handle a call to stat() made by JavaScript. 426 * 427 * stat expects 1 parameter: 428 * 0: The name of the file 429 * on success, stat returns a result in |output| separated by \1: 430 * 0: "stat" 431 * 1: the file name 432 * 2: the size of the file 433 * on failure, stat returns an error string in |output|. 434 * 435 * @param[in] num_params The number of params in |params|. 436 * @param[in] params An array of strings, parameters to this function. 437 * @param[out] output A string to write informational function output to. 438 * @return An errorcode; 0 means success, anything else is a failure. */ 439 int HandleStat(int num_params, char** params, char** output) { 440 const char* filename; 441 int result; 442 struct stat buf; 443 444 if (num_params != 1) { 445 *output = PrintfToNewString("Error: stat takes 1 parameter."); 446 return 1; 447 } 448 449 filename = params[0]; 450 451 memset(&buf, 0, sizeof(buf)); 452 result = stat(filename, &buf); 453 if (result == -1) { 454 *output = PrintfToNewString("Error: stat returned error %d.", errno); 455 return 2; 456 } 457 458 *output = PrintfToNewString("stat\1%s\1%d", filename, buf.st_size); 459 return 0; 460 } 461 462 /** 463 * Handle a call to opendir() made by JavaScript. 464 * 465 * opendir expects 1 parameter: 466 * 0: The name of the directory 467 * on success, opendir returns a result in |output| separated by \1: 468 * 0: "opendir" 469 * 1: the directory name 470 * 2: the index of the directory 471 * on failure, opendir returns an error string in |output|. 472 * 473 * @param[in] num_params The number of params in |params|. 474 * @param[in] params An array of strings, parameters to this function. 475 * @param[out] output A string to write informational function output to. 476 * @return An errorcode; 0 means success, anything else is a failure. */ 477 int HandleOpendir(int num_params, char** params, char** output) { 478 #if defined(WIN32) 479 *output = PrintfToNewString("Error: Win32 does not support opendir."); 480 return 1; 481 #else 482 DIR* dir; 483 int dir_index; 484 const char* dirname; 485 486 if (num_params != 1) { 487 *output = PrintfToNewString("Error: opendir takes 1 parameter."); 488 return 1; 489 } 490 491 dirname = params[0]; 492 493 dir = opendir(dirname); 494 if (!dir) { 495 *output = PrintfToNewString("Error: opendir returned a NULL DIR*."); 496 return 2; 497 } 498 499 dir_index = AddDirToMap(dir); 500 if (dir_index == -1) { 501 *output = PrintfToNewString( 502 "Error: Example only allows %d open dir handles.", MAX_OPEN_DIRS); 503 return 3; 504 } 505 506 *output = PrintfToNewString("opendir\1%s\1%d", dirname, dir_index); 507 return 0; 508 #endif 509 } 510 511 /** 512 * Handle a call to readdir() made by JavaScript. 513 * 514 * readdir expects 1 parameter: 515 * 0: The index of the directory (which is mapped to a DIR*) 516 * on success, opendir returns a result in |output| separated by \1: 517 * 0: "readdir" 518 * 1: the inode number of the entry 519 * 2: the name of the entry 520 * on failure, readdir returns an error string in |output|. 521 * 522 * @param[in] num_params The number of params in |params|. 523 * @param[in] params An array of strings, parameters to this function. 524 * @param[out] output A string to write informational function output to. 525 * @return An errorcode; 0 means success, anything else is a failure. */ 526 int HandleReaddir(int num_params, char** params, char** output) { 527 #if defined(WIN32) 528 *output = PrintfToNewString("Error: Win32 does not support readdir."); 529 return 1; 530 #else 531 DIR* dir; 532 const char* dir_index_string; 533 struct dirent* entry; 534 535 if (num_params != 1) { 536 *output = PrintfToNewString("Error: readdir takes 1 parameter."); 537 return 1; 538 } 539 540 dir_index_string = params[0]; 541 dir = GetDirFromIndexString(dir_index_string, NULL); 542 543 if (!dir) { 544 *output = PrintfToNewString("Error: Unknown dir handle %s.", 545 dir_index_string); 546 return 2; 547 } 548 549 entry = readdir(dir); 550 if (entry != NULL) { 551 *output = PrintfToNewString("readdir\1%s\1%d\1%s", dir_index_string, 552 entry->d_ino, entry->d_name); 553 } else { 554 *output = PrintfToNewString("readdir\1%s\1\1", dir_index_string); 555 } 556 557 return 0; 558 #endif 559 } 560 561 /** 562 * Handle a call to closedir() made by JavaScript. 563 * 564 * closedir expects 1 parameter: 565 * 0: The index of the directory (which is mapped to a DIR*) 566 * on success, closedir returns a result in |output| separated by \1: 567 * 0: "closedir" 568 * 1: the name of the directory 569 * on failure, closedir returns an error string in |output|. 570 * 571 * @param[in] num_params The number of params in |params|. 572 * @param[in] params An array of strings, parameters to this function. 573 * @param[out] output A string to write informational function output to. 574 * @return An errorcode; 0 means success, anything else is a failure. */ 575 int HandleClosedir(int num_params, char** params, char** output) { 576 #if defined(WIN32) 577 *output = PrintfToNewString("Error: Win32 does not support closedir."); 578 return 1; 579 #else 580 DIR* dir; 581 int dir_index; 582 const char* dir_index_string; 583 int result; 584 585 if (num_params != 1) { 586 *output = PrintfToNewString("Error: closedir takes 1 parameters."); 587 return 1; 588 } 589 590 dir_index_string = params[0]; 591 dir = GetDirFromIndexString(dir_index_string, &dir_index); 592 if (!dir) { 593 *output = PrintfToNewString("Error: Unknown dir handle %s.", 594 dir_index_string); 595 return 2; 596 } 597 598 result = closedir(dir); 599 if (result) { 600 *output = PrintfToNewString("Error: closedir returned error %d.", result); 601 return 3; 602 } 603 604 RemoveDirFromMap(dir_index); 605 606 *output = PrintfToNewString("closedir\1%s", dir_index_string); 607 return 0; 608 #endif 609 } 610 611 /** 612 * Handle a call to mkdir() made by JavaScript. 613 * 614 * mkdir expects 1 parameter: 615 * 0: The name of the directory 616 * 1: The mode to use for the new directory, in octal. 617 * on success, mkdir returns a result in |output| separated by \1: 618 * 0: "mkdir" 619 * 1: the name of the directory 620 * on failure, mkdir returns an error string in |output|. 621 * 622 * @param[in] num_params The number of params in |params|. 623 * @param[in] params An array of strings, parameters to this function. 624 * @param[out] output A string to write informational function output to. 625 * @return An errorcode; 0 means success, anything else is a failure. */ 626 int HandleMkdir(int num_params, char** params, char** output) { 627 const char* dirname; 628 int result; 629 int mode; 630 631 if (num_params != 2) { 632 *output = PrintfToNewString("Error: mkdir takes 2 parameters."); 633 return 1; 634 } 635 636 dirname = params[0]; 637 mode = strtol(params[1], NULL, 8); 638 639 result = mkdir(dirname, mode); 640 if (result != 0) { 641 *output = PrintfToNewString("Error: mkdir returned error: %d", errno); 642 return 2; 643 } 644 645 *output = PrintfToNewString("mkdir\1%s", dirname); 646 return 0; 647 } 648 649 /** 650 * Handle a call to gethostbyname() made by JavaScript. 651 * 652 * gethostbyname expects 1 parameter: 653 * 0: The name of the host to look up. 654 * on success, gethostbyname returns a result in |output| separated by \1: 655 * 0: "gethostbyname" 656 * 1: Host name 657 * 2: Address type (either "AF_INET" or "AF_INET6") 658 * 3. The first address. 659 * 4+ The second, third, etc. addresses. 660 * on failure, gethostbyname returns an error string in |output|. 661 * 662 * @param[in] num_params The number of params in |params|. 663 * @param[in] params An array of strings, parameters to this function. 664 * @param[out] output A string to write informational function output to. 665 * @return An errorcode; 0 means success, anything else is a failure. */ 666 int HandleGethostbyname(int num_params, char** params, char** output) { 667 struct hostent* info; 668 struct in_addr **addr_list; 669 const char* addr_type; 670 const char* name; 671 char inet6_addr_str[INET6_ADDRSTRLEN]; 672 int non_variable_len, output_len; 673 int current_pos; 674 int i; 675 676 if (num_params != 1) { 677 *output = PrintfToNewString("Error: gethostbyname takes 1 parameter."); 678 return 1; 679 } 680 681 name = params[0]; 682 683 info = gethostbyname(name); 684 if (!info) { 685 *output = PrintfToNewString("Error: gethostbyname failed, error is \"%s\"", 686 hstrerror(h_errno)); 687 return 2; 688 } 689 690 addr_type = info->h_addrtype == AF_INET ? "AF_INET" : "AF_INET6"; 691 692 non_variable_len = strlen("gethostbyname") + 1 693 + strlen(info->h_name) + 1 + strlen(addr_type); 694 output_len = non_variable_len; 695 696 addr_list = (struct in_addr **)info->h_addr_list; 697 for (i = 0; addr_list[i] != NULL; i++) { 698 output_len += 1; // for the divider 699 if (info->h_addrtype == AF_INET) { 700 output_len += strlen(inet_ntoa(*addr_list[i])); 701 } else { // IPv6 702 inet_ntop(AF_INET6, addr_list[i], inet6_addr_str, INET6_ADDRSTRLEN); 703 output_len += strlen(inet6_addr_str); 704 } 705 } 706 707 *output = (char*) calloc(output_len + 1, 1); 708 if (!*output) { 709 *output = PrintfToNewString("Error: out of memory."); 710 return 3; 711 } 712 snprintf(*output, non_variable_len + 1, "gethostbyname\1%s\1%s", 713 info->h_name, addr_type); 714 715 current_pos = non_variable_len; 716 for (i = 0; addr_list[i] != NULL; i++) { 717 if (info->h_addrtype == AF_INET) { 718 current_pos += sprintf(*output + current_pos, 719 "\1%s", inet_ntoa(*addr_list[i])); 720 } else { // IPv6 721 inet_ntop(AF_INET6, addr_list[i], inet6_addr_str, INET6_ADDRSTRLEN); 722 sprintf(*output + current_pos, "\1%s", inet6_addr_str); 723 } 724 } 725 return 0; 726 } 727