1 /* 2 * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /** 18 * # Filesystem Access 19 * 20 * The `fs` module provides functions for interacting with the file system. 21 * 22 * Functions can be individually imported and directly accessed using the 23 * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#named_import named import} 24 * syntax: 25 * 26 * ``` 27 * import { readlink, popen } from 'fs'; 28 * 29 * let dest = readlink('/sys/class/net/eth0'); 30 * let proc = popen('ps ww'); 31 * ``` 32 * 33 * Alternatively, the module namespace can be imported 34 * using a wildcard import statement: 35 * 36 * ``` 37 * import * as fs from 'fs'; 38 * 39 * let dest = fs.readlink('/sys/class/net/eth0'); 40 * let proc = fs.popen('ps ww'); 41 * ``` 42 * 43 * Additionally, the filesystem module namespace may also be imported by invoking 44 * the `ucode` interpreter with the `-lfs` switch. 45 * 46 * @module fs 47 */ 48 49 #include <stdio.h> 50 #include <errno.h> 51 #include <string.h> 52 #include <dirent.h> 53 #include <unistd.h> 54 #include <sys/stat.h> 55 #include <sys/types.h> 56 #include <sys/file.h> 57 #include <grp.h> 58 #include <pwd.h> 59 #include <glob.h> 60 #include <fnmatch.h> 61 #include <limits.h> 62 #include <fcntl.h> 63 #include <sys/statvfs.h> /* statvfs(3) */ 64 65 #if defined(__linux__) 66 #include <sys/statfs.h> /* statfs() for f_type */ 67 #define HAS_IOCTL 68 #endif 69 70 #ifdef HAS_IOCTL 71 #include <sys/ioctl.h> 72 73 #define IOC_DIR_NONE (_IOC_NONE) 74 #define IOC_DIR_READ (_IOC_READ) 75 #define IOC_DIR_WRITE (_IOC_WRITE) 76 #define IOC_DIR_RW (_IOC_READ | _IOC_WRITE) 77 78 #endif 79 80 #include "ucode/module.h" 81 #include "ucode/platform.h" 82 83 #define err_return(err) do { \ 84 uc_vm_registry_set(vm, "fs.last_error", ucv_int64_new(err)); \ 85 return NULL; \ 86 } while(0) 87 88 static int 89 get_fd(uc_vm_t *vm, uc_value_t *val) 90 { 91 uc_value_t *fn; 92 int64_t n; 93 94 fn = ucv_property_get(val, "fileno"); 95 errno = 0; 96 97 if (ucv_is_callable(fn)) { 98 uc_vm_stack_push(vm, ucv_get(val)); 99 uc_vm_stack_push(vm, ucv_get(fn)); 100 101 if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE) 102 return -1; 103 104 val = uc_vm_stack_pop(vm); 105 n = ucv_int64_get(val); 106 ucv_put(val); 107 } 108 else { 109 n = ucv_int64_get(val); 110 } 111 112 if (errno || n < 0 || n > (int64_t)INT_MAX) 113 return -1; 114 115 return (int)n; 116 } 117 118 119 /** 120 * Query error information. 121 * 122 * Returns a string containing a description of the last occurred error or 123 * `null` if there is no error information. 124 * 125 * @function module:fs#error 126 * 127 * 128 * @returns {?string} 129 * 130 * @example 131 * // Trigger file system error 132 * unlink('/path/does/not/exist'); 133 * 134 * // Print error (should yield "No such file or directory") 135 * print(error(), "\n"); 136 */ 137 static uc_value_t * 138 uc_fs_error(uc_vm_t *vm, size_t nargs) 139 { 140 int last_error = ucv_int64_get(uc_vm_registry_get(vm, "fs.last_error")); 141 142 if (last_error == 0) 143 return NULL; 144 145 uc_vm_registry_set(vm, "fs.last_error", ucv_int64_new(0)); 146 147 return ucv_string_new(strerror(last_error)); 148 } 149 150 static uc_value_t * 151 uc_fs_read_common(uc_vm_t *vm, size_t nargs, const char *type) 152 { 153 uc_value_t *limit = uc_fn_arg(0); 154 uc_value_t *rv = NULL; 155 char buf[128], *p = NULL, *tmp; 156 size_t rlen, len = 0; 157 const char *lstr; 158 int64_t lsize; 159 ssize_t llen; 160 161 FILE **fp = uc_fn_this(type); 162 163 if (!fp || !*fp) 164 err_return(EBADF); 165 166 if (ucv_type(limit) == UC_STRING) { 167 lstr = ucv_string_get(limit); 168 llen = ucv_string_length(limit); 169 170 if (llen == 4 && !strcmp(lstr, "line")) { 171 llen = getline(&p, &rlen, *fp); 172 173 if (llen == -1) { 174 free(p); 175 err_return(errno); 176 } 177 178 len = (size_t)llen; 179 } 180 else if (llen == 3 && !strcmp(lstr, "all")) { 181 while (true) { 182 rlen = fread(buf, 1, sizeof(buf), *fp); 183 184 tmp = realloc(p, len + rlen); 185 186 if (!tmp) { 187 free(p); 188 err_return(ENOMEM); 189 } 190 191 memcpy(tmp + len, buf, rlen); 192 193 p = tmp; 194 len += rlen; 195 196 if (rlen == 0) 197 break; 198 } 199 } 200 else if (llen == 1) { 201 llen = getdelim(&p, &rlen, *lstr, *fp); 202 203 if (llen == -1) { 204 free(p); 205 err_return(errno); 206 } 207 208 len = (size_t)llen; 209 } 210 else { 211 return NULL; 212 } 213 } 214 else if (ucv_type(limit) == UC_INTEGER) { 215 lsize = ucv_int64_get(limit); 216 217 if (lsize <= 0) 218 return NULL; 219 220 p = calloc(1, lsize); 221 222 if (!p) 223 err_return(ENOMEM); 224 225 len = fread(p, 1, lsize, *fp); 226 227 if (ferror(*fp)) { 228 free(p); 229 err_return(errno); 230 } 231 } 232 else { 233 err_return(EINVAL); 234 } 235 236 rv = ucv_string_new_length(p, len); 237 free(p); 238 239 return rv; 240 } 241 242 static uc_value_t * 243 uc_fs_write_common(uc_vm_t *vm, size_t nargs, const char *type) 244 { 245 uc_value_t *data = uc_fn_arg(0); 246 size_t len, wsize; 247 char *str; 248 249 FILE **fp = uc_fn_this(type); 250 251 if (!fp || !*fp) 252 err_return(EBADF); 253 254 if (ucv_type(data) == UC_STRING) { 255 len = ucv_string_length(data); 256 wsize = fwrite(ucv_string_get(data), 1, len, *fp); 257 } 258 else { 259 str = ucv_to_jsonstring(vm, data); 260 len = str ? strlen(str) : 0; 261 wsize = fwrite(str, 1, len, *fp); 262 free(str); 263 } 264 265 if (wsize < len && ferror(*fp)) 266 err_return(errno); 267 268 return ucv_int64_new(wsize); 269 } 270 271 static uc_value_t * 272 uc_fs_flush_common(uc_vm_t *vm, size_t nargs, const char *type) 273 { 274 FILE **fp = uc_fn_this(type); 275 276 if (!fp || !*fp) 277 err_return(EBADF); 278 279 if (fflush(*fp) == EOF) 280 err_return(errno); 281 282 return ucv_boolean_new(true); 283 } 284 285 static uc_value_t * 286 uc_fs_fileno_common(uc_vm_t *vm, size_t nargs, const char *type) 287 { 288 int fd; 289 290 FILE **fp = uc_fn_this(type); 291 292 if (!fp || !*fp) 293 err_return(EBADF); 294 295 fd = fileno(*fp); 296 297 if (fd == -1) 298 err_return(errno); 299 300 return ucv_int64_new(fd); 301 } 302 303 304 /** 305 * Represents a handle for interacting with a program launched by `popen()`. 306 * 307 * @class module:fs.proc 308 * @hideconstructor 309 * 310 * @borrows module:fs#error as module:fs.proc#error 311 * 312 * @see {@link module:fs#popen|popen()} 313 * 314 * @example 315 * 316 * const handle = popen(…); 317 * 318 * handle.read(…); 319 * handle.write(…); 320 * handle.flush(); 321 * 322 * handle.fileno(); 323 * 324 * handle.close(); 325 * 326 * handle.error(); 327 */ 328 329 /** 330 * Closes the program handle and awaits program termination. 331 * 332 * Upon calling `close()` on the handle, the program's input or output stream 333 * (depending on the open mode) is closed. Afterwards, the function awaits the 334 * termination of the underlying program and returns its exit code. 335 * 336 * - When the program was terminated by a signal, the return value will be the 337 * negative signal number, e.g. `-9` for SIGKILL. 338 * 339 * - When the program terminated normally, the return value will be the positive 340 * exit code of the program. 341 * 342 * Returns a negative signal number if the program was terminated by a signal. 343 * 344 * Returns a positive exit code if the program terminated normally. 345 * 346 * Returns `null` if an error occurred. 347 * 348 * @function module:fs.proc#close 349 * 350 * @returns {?number} 351 */ 352 static uc_value_t * 353 uc_fs_pclose(uc_vm_t *vm, size_t nargs) 354 { 355 FILE **fp = uc_fn_this("fs.proc"); 356 int rc; 357 358 if (!fp || !*fp) 359 err_return(EBADF); 360 361 rc = pclose(*fp); 362 *fp = NULL; 363 364 if (rc == -1) 365 err_return(errno); 366 367 if (WIFEXITED(rc)) 368 return ucv_int64_new(WEXITSTATUS(rc)); 369 370 if (WIFSIGNALED(rc)) 371 return ucv_int64_new(-WTERMSIG(rc)); 372 373 return ucv_int64_new(0); 374 } 375 376 /** 377 * Reads a chunk of data from the program handle. 378 * 379 * The length argument may be either a positive number of bytes to read, in 380 * which case the read call returns up to that many bytes, or a string to 381 * specify a dynamic read size. 382 * 383 * - If length is a number, the method will read the specified number of bytes 384 * from the handle. Reading stops after the given amount of bytes or after 385 * encountering EOF, whatever comes first. 386 * 387 * - If length is the string "line", the method will read an entire line, 388 * terminated by "\n" (a newline), from the handle. Reading stops at the next 389 * newline or when encountering EOF. The returned data will contain the 390 * terminating newline character if one was read. 391 * 392 * - If length is the string "all", the method will read from the handle until 393 * encountering EOF and return the complete contents. 394 * 395 * - If length is a single character string, the method will read from the 396 * handle until encountering the specified character or upon encountering 397 * EOF. The returned data will contain the terminating character if one was 398 * read. 399 * 400 * Returns a string containing the read data. 401 * 402 * Returns an empty string on EOF. 403 * 404 * Returns `null` if a read error occurred. 405 * 406 * @function module:fs.proc#read 407 * 408 * @param {number|string} length 409 * The length of data to read. Can be a number, the string "line", the string 410 * "all", or a single character string. 411 * 412 * @returns {?string} 413 * 414 * @example 415 * const fp = popen("command", "r"); 416 * 417 * // Example 1: Read 10 bytes from the handle 418 * const chunk = fp.read(10); 419 * 420 * // Example 2: Read the handle line by line 421 * for (let line = fp.read("line"); length(line); line = fp.read("line")) 422 * print(line); 423 * 424 * // Example 3: Read the complete contents from the handle 425 * const content = fp.read("all"); 426 * 427 * // Example 4: Read until encountering the character ':' 428 * const field = fp.read(":"); 429 */ 430 static uc_value_t * 431 uc_fs_pread(uc_vm_t *vm, size_t nargs) 432 { 433 return uc_fs_read_common(vm, nargs, "fs.proc"); 434 } 435 436 /** 437 * Writes a chunk of data to the program handle. 438 * 439 * In case the given data is not a string, it is converted to a string before 440 * being written to the program's stdin. String values are written as-is, 441 * integer and double values are written in decimal notation, boolean values are 442 * written as `true` or `false` while arrays and objects are converted to their 443 * JSON representation before being written. The `null` value is represented by 444 * an empty string so `proc.write(null)` would be a no-op. Resource values are 445 * written in the form `<type address>`, e.g. `<fs.file 0x7f60f0981760>`. 446 * 447 * If resource, array or object values contain a `tostring()` function in their 448 * prototypes, then this function is invoked to obtain an alternative string 449 * representation of the value. 450 * 451 * Returns the number of bytes written. 452 * 453 * Returns `null` if a write error occurred. 454 * 455 * @function module:fs.proc#write 456 * 457 * @param {*} data 458 * The data to be written. 459 * 460 * @returns {?number} 461 * 462 * @example 463 * const fp = popen("command", "w"); 464 * 465 * fp.write("Hello world!\n"); 466 */ 467 static uc_value_t * 468 uc_fs_pwrite(uc_vm_t *vm, size_t nargs) 469 { 470 return uc_fs_write_common(vm, nargs, "fs.proc"); 471 } 472 473 /** 474 * Forces a write of all buffered data to the underlying handle. 475 * 476 * Returns `true` if the data was successfully flushed. 477 * 478 * Returns `null` on error. 479 * 480 * @function module:fs.proc#flush 481 * 482 * @returns {?boolean} 483 * 484 */ 485 static uc_value_t * 486 uc_fs_pflush(uc_vm_t *vm, size_t nargs) 487 { 488 return uc_fs_flush_common(vm, nargs, "fs.proc"); 489 } 490 491 /** 492 * Obtains the number of the handle's underlying file descriptor. 493 * 494 * Returns the descriptor number. 495 * 496 * Returns `null` on error. 497 * 498 * @function module:fs.proc#fileno 499 * 500 * @returns {?number} 501 */ 502 static uc_value_t * 503 uc_fs_pfileno(uc_vm_t *vm, size_t nargs) 504 { 505 return uc_fs_fileno_common(vm, nargs, "fs.proc"); 506 } 507 508 /** 509 * Starts a process and returns a handle representing the executed process. 510 * 511 * The handle will be connected to the process stdin or stdout, depending on the 512 * value of the mode argument. 513 * 514 * The mode argument may be either "r" to open the process for reading (connect 515 * to its stdout) or "w" to open the process for writing (connect to its stdin). 516 * 517 * The mode character "r" or "w" may be optionally followed by "e" to apply the 518 * FD_CLOEXEC flag onto the open descriptor. 519 * 520 * Returns a process handle referring to the executed process. 521 * 522 * Returns `null` if an error occurred. 523 * 524 * @function module:fs#popen 525 * 526 * @param {string} command 527 * The command to be executed. 528 * 529 * @param {string} [mode="r"] 530 * The open mode of the process handle. 531 * 532 * @returns {?module:fs.proc} 533 * 534 * @example 535 * // Open a process 536 * const process = popen('command', 'r'); 537 */ 538 static uc_value_t * 539 uc_fs_popen(uc_vm_t *vm, size_t nargs) 540 { 541 uc_value_t *comm = uc_fn_arg(0); 542 uc_value_t *mode = uc_fn_arg(1); 543 FILE *fp; 544 545 if (ucv_type(comm) != UC_STRING) 546 err_return(EINVAL); 547 548 fp = popen(ucv_string_get(comm), 549 ucv_type(mode) == UC_STRING ? ucv_string_get(mode) : "r"); 550 551 if (!fp) 552 err_return(errno); 553 554 return ucv_resource_create(vm, "fs.proc", fp); 555 } 556 557 558 /** 559 * Represents a handle for interacting with a file opened by one of the file 560 * open functions. 561 * 562 * @class module:fs.file 563 * @hideconstructor 564 * 565 * @borrows module:fs#error as module:fs.file#error 566 * 567 * @see {@link module:fs#open|open()} 568 * @see {@link module:fs#fdopen|fdopen()} 569 * @see {@link module:fs#mkstemp|mkstemp()} 570 * @see {@link module:fs#pipe|pipe()} 571 * 572 * @example 573 * 574 * const handle = open(…); 575 * 576 * handle.read(…); 577 * handle.write(…); 578 * handle.flush(); 579 * 580 * handle.seek(…); 581 * handle.tell(); 582 * 583 * handle.isatty(); 584 * handle.fileno(); 585 * 586 * handle.close(); 587 * 588 * handle.error(); 589 */ 590 591 /** 592 * Closes the file handle. 593 * 594 * Upon calling `close()` on the handle, buffered data is flushed and the 595 * underlying file descriptor is closed. 596 * 597 * Returns `true` if the handle was properly closed. 598 * 599 * Returns `null` if an error occurred. 600 * 601 * @function module:fs.file#close 602 * 603 * @returns {?boolean} 604 */ 605 static uc_value_t * 606 uc_fs_close(uc_vm_t *vm, size_t nargs) 607 { 608 FILE **fp = uc_fn_this("fs.file"); 609 610 if (!fp || !*fp) 611 err_return(EBADF); 612 613 fclose(*fp); 614 *fp = NULL; 615 616 return ucv_boolean_new(true); 617 } 618 619 /** 620 * Reads a chunk of data from the file handle. 621 * 622 * The length argument may be either a positive number of bytes to read, in 623 * which case the read call returns up to that many bytes, or a string to 624 * specify a dynamic read size. 625 * 626 * - If length is a number, the method will read the specified number of bytes 627 * from the handle. Reading stops after the given amount of bytes or after 628 * encountering EOF, whatever comes first. 629 * 630 * - If length is the string "line", the method will read an entire line, 631 * terminated by "\n" (a newline), from the handle. Reading stops at the next 632 * newline or when encountering EOF. The returned data will contain the 633 * terminating newline character if one was read. 634 * 635 * - If length is the string "all", the method will read from the handle until 636 * encountering EOF and return the complete contents. 637 * 638 * - If length is a single character string, the method will read from the 639 * handle until encountering the specified character or upon encountering 640 * EOF. The returned data will contain the terminating character if one was 641 * read. 642 * 643 * Returns a string containing the read data. 644 * 645 * Returns an empty string on EOF. 646 * 647 * Returns `null` if a read error occurred. 648 * 649 * @function module:fs.file#read 650 * 651 * @param {number|string} length 652 * The length of data to read. Can be a number, the string "line", the string 653 * "all", or a single character string. 654 * 655 * @returns {?string} 656 * 657 * @example 658 * const fp = open("file.txt", "r"); 659 * 660 * // Example 1: Read 10 bytes from the handle 661 * const chunk = fp.read(10); 662 * 663 * // Example 2: Read the handle line by line 664 * for (let line = fp.read("line"); length(line); line = fp.read("line")) 665 * print(line); 666 * 667 * // Example 3: Read the complete contents from the handle 668 * const content = fp.read("all"); 669 * 670 * // Example 4: Read until encountering the character ':' 671 * const field = fp.read(":"); 672 */ 673 static uc_value_t * 674 uc_fs_read(uc_vm_t *vm, size_t nargs) 675 { 676 return uc_fs_read_common(vm, nargs, "fs.file"); 677 } 678 679 /** 680 * Writes a chunk of data to the file handle. 681 * 682 * In case the given data is not a string, it is converted to a string before 683 * being written into the file. String values are written as-is, integer and 684 * double values are written in decimal notation, boolean values are written as 685 * `true` or `false` while arrays and objects are converted to their JSON 686 * representation before being written. The `null` value is represented by an 687 * empty string so `file.write(null)` would be a no-op. Resource values are 688 * written in the form `<type address>`, e.g. `<fs.file 0x7f60f0981760>`. 689 * 690 * If resource, array or object values contain a `tostring()` function in their 691 * prototypes, then this function is invoked to obtain an alternative string 692 * representation of the value. 693 * 694 * Returns the number of bytes written. 695 * 696 * Returns `null` if a write error occurred. 697 * 698 * @function module:fs.file#write 699 * 700 * @param {*} data 701 * The data to be written. 702 * 703 * @returns {?number} 704 * 705 * @example 706 * const fp = open("file.txt", "w"); 707 * 708 * fp.write("Hello world!\n"); 709 */ 710 static uc_value_t * 711 uc_fs_write(uc_vm_t *vm, size_t nargs) 712 { 713 return uc_fs_write_common(vm, nargs, "fs.file"); 714 } 715 716 /** 717 * Set file read position. 718 * 719 * Set the read position of the open file handle to the given offset and 720 * position. 721 * 722 * Returns `true` if the read position was set. 723 * 724 * Returns `null` if an error occurred. 725 * 726 * @function module:fs.file#seek 727 * 728 * @param {number} [offset=0] 729 * The offset in bytes. 730 * 731 * @param {number} [position=0] 732 * The position of the offset. 733 * 734 * | Position | Description | 735 * |----------|----------------------------------------------------------------------------------------------| 736 * | `0` | The given offset is relative to the start of the file. This is the default value if omitted. | 737 * | `1` | The given offset is relative to the current read position. | 738 * | `2` | The given offset is relative to the end of the file. | 739 * 740 * @returns {?boolean} 741 * 742 * @example 743 * const fp = open("file.txt", "r"); 744 * 745 * print(fp.read(100), "\n"); // read 100 bytes... 746 * fp.seek(0, 0); // ... and reset position to start of file 747 * print(fp.read(100), "\n"); // ... read same 100 bytes again 748 * 749 * fp.seek(10, 1); // skip 10 bytes forward, relative to current offset ... 750 * fp.tell(); // ... position is at 110 now 751 * 752 * fp.seek(-10, 2); // set position to ten bytes before EOF ... 753 * print(fp.read(100), "\n"); // ... reads 10 bytes at most 754 */ 755 static uc_value_t * 756 uc_fs_seek(uc_vm_t *vm, size_t nargs) 757 { 758 uc_value_t *ofs = uc_fn_arg(0); 759 uc_value_t *how = uc_fn_arg(1); 760 int whence, res; 761 off_t offset; 762 763 FILE **fp = uc_fn_this("fs.file"); 764 765 if (!fp || !*fp) 766 err_return(EBADF); 767 768 if (!ofs) 769 offset = 0; 770 else if (ucv_type(ofs) != UC_INTEGER) 771 err_return(EINVAL); 772 else 773 offset = (off_t)ucv_int64_get(ofs); 774 775 if (!how) 776 whence = 0; 777 else if (ucv_type(how) != UC_INTEGER) 778 err_return(EINVAL); 779 else 780 whence = (int)ucv_int64_get(how); 781 782 res = fseeko(*fp, offset, whence); 783 784 if (res < 0) 785 err_return(errno); 786 787 return ucv_boolean_new(true); 788 } 789 790 /** 791 * Truncate file to a given size 792 * 793 * Returns `true` if the file was successfully truncated. 794 * 795 * Returns `null` if an error occurred. 796 * 797 * @function module:fs.file#truncate 798 * 799 * @param {number} [offset=0] 800 * The offset in bytes. 801 * 802 * @returns {?boolean} 803 */ 804 static uc_value_t * 805 uc_fs_truncate(uc_vm_t *vm, size_t nargs) 806 { 807 FILE *fp = uc_fn_thisval("fs.file"); 808 uc_value_t *ofs = uc_fn_arg(0); 809 off_t offset; 810 811 if (!fp) 812 err_return(EBADF); 813 814 if (!ofs) 815 offset = 0; 816 else if (ucv_type(ofs) != UC_INTEGER) 817 err_return(EINVAL); 818 else 819 offset = (off_t)ucv_int64_get(ofs); 820 821 if (ftruncate(fileno(fp), offset) < 0) 822 err_return(errno); 823 824 return ucv_boolean_new(true); 825 } 826 827 /** 828 * Locks or unlocks a file. 829 * 830 * The mode argument specifies lock/unlock operation flags. 831 * 832 * | Flag | Description | 833 * |---------|------------------------------| 834 * | "s" | shared lock | 835 * | "x" | exclusive lock | 836 * | "n" | don't block when locking | 837 * | "u" | unlock | 838 * 839 * Returns `true` if the file was successfully locked/unlocked. 840 * 841 * Returns `null` if an error occurred. 842 * 843 * @function module:fs.file#lock 844 * 845 * @param {string} [op] 846 * The lock operation flags 847 * 848 * @returns {?boolean} 849 */ 850 static uc_value_t * 851 uc_fs_lock(uc_vm_t *vm, size_t nargs) 852 { 853 FILE *fp = uc_fn_thisval("fs.file"); 854 uc_value_t *mode = uc_fn_arg(0); 855 int i, op = 0; 856 char *m; 857 858 if (!fp) 859 err_return(EBADF); 860 861 if (ucv_type(mode) != UC_STRING) 862 err_return(EINVAL); 863 864 m = ucv_string_get(mode); 865 for (i = 0; m[i]; i++) { 866 switch (m[i]) { 867 case 's': op |= LOCK_SH; break; 868 case 'x': op |= LOCK_EX; break; 869 case 'n': op |= LOCK_NB; break; 870 case 'u': op |= LOCK_UN; break; 871 default: err_return(EINVAL); 872 } 873 } 874 875 if (flock(fileno(fp), op) < 0) 876 err_return(errno); 877 878 return ucv_boolean_new(true); 879 } 880 881 /** 882 * Obtain current read position. 883 * 884 * Obtains the current, absolute read position of the open file. 885 * 886 * Returns an integer containing the current read offset in bytes. 887 * 888 * Returns `null` if an error occurred. 889 * 890 * @function module:fs.file#tell 891 * 892 * @returns {?number} 893 */ 894 static uc_value_t * 895 uc_fs_tell(uc_vm_t *vm, size_t nargs) 896 { 897 off_t offset; 898 899 FILE **fp = uc_fn_this("fs.file"); 900 901 if (!fp || !*fp) 902 err_return(EBADF); 903 904 offset = ftello(*fp); 905 906 if (offset < 0) 907 err_return(errno); 908 909 return ucv_int64_new(offset); 910 } 911 912 /** 913 * Check for TTY. 914 * 915 * Checks whether the open file handle refers to a TTY (terminal) device. 916 * 917 * Returns `true` if the handle refers to a terminal. 918 * 919 * Returns `false` if the handle refers to another kind of file. 920 * 921 * Returns `null` on error. 922 * 923 * @function module:fs.file#isatty 924 * 925 * @returns {?boolean} 926 * 927 */ 928 static uc_value_t * 929 uc_fs_isatty(uc_vm_t *vm, size_t nargs) 930 { 931 FILE **fp = uc_fn_this("fs.file"); 932 int fd; 933 934 if (!fp || !*fp) 935 err_return(EBADF); 936 937 fd = fileno(*fp); 938 939 if (fd == -1) 940 err_return(errno); 941 942 return ucv_boolean_new(isatty(fd) == 1); 943 } 944 945 /** 946 * Forces a write of all buffered data to the underlying handle. 947 * 948 * Returns `true` if the data was successfully flushed. 949 * 950 * Returns `null` on error. 951 * 952 * @function module:fs.file#flush 953 * 954 * @returns {?boolean} 955 * 956 */ 957 static uc_value_t * 958 uc_fs_flush(uc_vm_t *vm, size_t nargs) 959 { 960 return uc_fs_flush_common(vm, nargs, "fs.file"); 961 } 962 963 /** 964 * Obtains the number of the handle's underlying file descriptor. 965 * 966 * Returns the descriptor number. 967 * 968 * Returns `null` on error. 969 * 970 * @function module:fs.file#fileno 971 * 972 * @returns {?number} 973 */ 974 static uc_value_t * 975 uc_fs_fileno(uc_vm_t *vm, size_t nargs) 976 { 977 return uc_fs_fileno_common(vm, nargs, "fs.file"); 978 } 979 980 #ifdef HAS_IOCTL 981 982 /** 983 * Performs an ioctl operation on the file. 984 * 985 * The direction parameter specifies who is reading and writing, 986 * from the user's point of view. It can be one of the following values: 987 * 988 * | Direction | Description | 989 * |----------------|-----------------------------------------------------------------------------------| 990 * | IOC_DIR_NONE | neither userspace nor kernel is writing, ioctl is executed without passing data. | 991 * | IOC_DIR_WRITE | userspace is writing and kernel is reading. | 992 * | IOC_DIR_READ | kernel is writing and userspace is reading. | 993 * | IOC_DIR_RW | userspace is writing and kernel is writing back into the data structure. | 994 * 995 * Returns the result of the ioctl operation; for `IOC_DIR_READ` and 996 * `IOC_DIR_RW` this is a string containing the data, otherwise a number as 997 * return code. 998 * 999 * In case of an error, null is returned and error details are available via 1000 * {@link module:fs#error|error()}. 1001 * 1002 * @function module:fs.file#ioctl 1003 * 1004 * @param {number} direction 1005 * The direction of the ioctl operation. Use constants IOC_DIR_*. 1006 * 1007 * @param {number} type 1008 * The ioctl type (see https://www.kernel.org/doc/html/latest/userspace-api/ioctl/ioctl-number.html) 1009 * 1010 * @param {number} num 1011 * The ioctl sequence number. 1012 * 1013 * @param {number|string} [value] 1014 * The value to pass to the ioctl system call. For `IOC_DIR_NONE`, this argument 1015 * is ignored. With `IOC_DIR_READ`, the value should be a positive integer 1016 * specifying the number of bytes to expect from the kernel. For the other 1017 * directions, `IOC_DIR_WRITE` and `IOC_DIR_RW`, that value parameter must be a 1018 * string, serving as buffer for the data to send. 1019 * 1020 * @returns {?number|?string} 1021 */ 1022 static uc_value_t * 1023 uc_fs_ioctl(uc_vm_t *vm, size_t nargs) 1024 { 1025 FILE *fp = uc_fn_thisval("fs.file"); 1026 uc_value_t *direction = uc_fn_arg(0); 1027 uc_value_t *type = uc_fn_arg(1); 1028 uc_value_t *num = uc_fn_arg(2); 1029 uc_value_t *value = uc_fn_arg(3); 1030 uc_value_t *mem = NULL; 1031 char *buf = NULL; 1032 unsigned long req = 0; 1033 unsigned int dir, ty, nr; 1034 size_t sz = 0; 1035 int fd, ret; 1036 1037 if (!fp) 1038 err_return(EBADF); 1039 1040 fd = fileno(fp); 1041 if (fd == -1) 1042 err_return(EBADF); 1043 1044 if (ucv_type(direction) != UC_INTEGER || ucv_type(type) != UC_INTEGER || 1045 ucv_type(num) != UC_INTEGER) 1046 err_return(EINVAL); 1047 1048 dir = ucv_uint64_get(direction); 1049 ty = ucv_uint64_get(type); 1050 nr = ucv_uint64_get(num); 1051 1052 switch (dir) { 1053 case IOC_DIR_NONE: 1054 break; 1055 1056 case IOC_DIR_WRITE: 1057 if (ucv_type(value) != UC_STRING) 1058 err_return(EINVAL); 1059 1060 sz = ucv_string_length(value); 1061 buf = ucv_string_get(value); 1062 break; 1063 1064 case IOC_DIR_READ: 1065 if (ucv_type(value) != UC_INTEGER) 1066 err_return(EINVAL); 1067 1068 sz = ucv_to_unsigned(value); 1069 1070 if (errno != 0) 1071 err_return(errno); 1072 1073 mem = xalloc(sizeof(uc_string_t) + sz + 1); 1074 mem->type = UC_STRING; 1075 mem->refcount = 1; 1076 buf = ucv_string_get(mem); 1077 ((uc_string_t *)mem)->length = sz; 1078 break; 1079 1080 case IOC_DIR_RW: 1081 if (ucv_type(value) != UC_STRING) 1082 err_return(EINVAL); 1083 1084 sz = ucv_string_length(value); 1085 mem = ucv_string_new_length(ucv_string_get(value), sz); 1086 buf = ucv_string_get(mem); 1087 break; 1088 1089 default: 1090 err_return(EINVAL); 1091 } 1092 1093 req = _IOC(dir, ty, nr, sz); 1094 ret = ioctl(fd, req, buf); 1095 1096 if (ret < 0) { 1097 ucv_put(mem); 1098 err_return(errno); 1099 } 1100 1101 return mem ? mem : ucv_uint64_new(ret); 1102 } 1103 1104 #endif 1105 1106 /** 1107 * Opens a file. 1108 * 1109 * The mode argument specifies the way the file is opened, it may 1110 * start with one of the following values: 1111 * 1112 * | Mode | Description | 1113 * |---------|---------------------------------------------------------------------------------------------------------------| 1114 * | "r" | Opens a file for reading. The file must exist. | 1115 * | "w" | Opens a file for writing. If the file exists, it is truncated. If the file does not exist, it is created. | 1116 * | "a" | Opens a file for appending. Data is written at the end of the file. If the file does not exist, it is created. | 1117 * | "r+" | Opens a file for both reading and writing. The file must exist. | 1118 * | "w+" | Opens a file for both reading and writing. If the file exists, it is truncated. If the file does not exist, it is created. | 1119 * | "a+" | Opens a file for both reading and appending. Data can be read and written at the end of the file. If the file does not exist, it is created. | 1120 * 1121 * Additionally, the following flag characters may be appended to 1122 * the mode value: 1123 * 1124 * | Flag | Description | 1125 * |---------|---------------------------------------------------------------------------------------------------------------| 1126 * | "x" | Opens a file for exclusive creation. If the file exists, the `open` call fails. | 1127 * | "e" | Opens a file with the `O_CLOEXEC` flag set, ensuring that the file descriptor is closed on `exec` calls. | 1128 * 1129 * If the mode is one of `"w…"` or `"a…"`, the permission argument 1130 * controls the filesystem permissions bits used when creating 1131 * the file. 1132 * 1133 * Returns a file handle object associated with the opened file. 1134 * 1135 * @function module:fs#open 1136 * 1137 * @param {string} path 1138 * The path to the file. 1139 * 1140 * @param {string} [mode="r"] 1141 * The file opening mode. 1142 * 1143 * @param {number} [perm=0o666] 1144 * The file creation permissions (for modes `w…` and `a…`) 1145 * 1146 * @returns {?module:fs.file} 1147 * 1148 * @example 1149 * // Open a file in read-only mode 1150 * const fileHandle = open('file.txt', 'r'); 1151 */ 1152 static uc_value_t * 1153 uc_fs_open(uc_vm_t *vm, size_t nargs) 1154 { 1155 int open_mode, open_flags, fd, i; 1156 uc_value_t *path = uc_fn_arg(0); 1157 uc_value_t *mode = uc_fn_arg(1); 1158 uc_value_t *perm = uc_fn_arg(2); 1159 mode_t open_perm = 0666; 1160 FILE *fp; 1161 char *m; 1162 1163 if (ucv_type(path) != UC_STRING) 1164 err_return(EINVAL); 1165 1166 m = (ucv_type(mode) == UC_STRING) ? ucv_string_get(mode) : "r"; 1167 1168 switch (*m) { 1169 case 'r': 1170 open_mode = O_RDONLY; 1171 open_flags = 0; 1172 break; 1173 1174 case 'w': 1175 open_mode = O_WRONLY; 1176 open_flags = O_CREAT | O_TRUNC; 1177 break; 1178 1179 case 'a': 1180 open_mode = O_WRONLY; 1181 open_flags = O_CREAT | O_APPEND; 1182 break; 1183 1184 default: 1185 err_return(EINVAL); 1186 } 1187 1188 for (i = 1; m[i]; i++) { 1189 switch (m[i]) { 1190 case '+': open_mode = O_RDWR; break; 1191 case 'x': open_flags |= O_EXCL; break; 1192 case 'e': open_flags |= O_CLOEXEC; break; 1193 } 1194 } 1195 1196 if (perm) { 1197 if (ucv_type(perm) != UC_INTEGER) 1198 err_return(EINVAL); 1199 1200 open_perm = ucv_int64_get(perm); 1201 } 1202 1203 #ifdef O_LARGEFILE 1204 open_flags |= open_mode | O_LARGEFILE; 1205 #else 1206 open_flags |= open_mode; 1207 #endif 1208 1209 fd = open(ucv_string_get(path), open_flags, open_perm); 1210 1211 if (fd < 0) 1212 return NULL; 1213 1214 fp = fdopen(fd, m); 1215 1216 if (!fp) { 1217 i = errno; 1218 close(fd); 1219 err_return(i); 1220 } 1221 1222 return ucv_resource_create(vm, "fs.file", fp); 1223 } 1224 1225 /** 1226 * Associates a file descriptor number with a file handle object. 1227 * 1228 * The mode argument controls how the file handle object is opened 1229 * and must match the open mode of the underlying descriptor. 1230 * 1231 * It may be set to one of the following values: 1232 * 1233 * | Mode | Description | 1234 * |---------|--------------------------------------------------------------------------------------------------------------| 1235 * | "r" | Opens a file stream for reading. The file descriptor must be valid and opened in read mode. | 1236 * | "w" | Opens a file stream for writing. The file descriptor must be valid and opened in write mode. | 1237 * | "a" | Opens a file stream for appending. The file descriptor must be valid and opened in write mode. | 1238 * | "r+" | Opens a file stream for both reading and writing. The file descriptor must be valid and opened in read/write mode. | 1239 * | "w+" | Opens a file stream for both reading and writing. The file descriptor must be valid and opened in read/write mode. | 1240 * | "a+" | Opens a file stream for both reading and appending. The file descriptor must be valid and opened in read/write mode. | 1241 * 1242 * Returns the file handle object associated with the file descriptor. 1243 * 1244 * @function module:fs#fdopen 1245 * 1246 * @param {number} fd 1247 * The file descriptor. 1248 * 1249 * @param {string} [mode="r"] 1250 * The open mode. 1251 * 1252 * @returns {Object} 1253 * 1254 * @example 1255 * // Associate file descriptors of stdin and stdout with handles 1256 * const stdinHandle = fdopen(0, 'r'); 1257 * const stdoutHandle = fdopen(1, 'w'); 1258 */ 1259 static uc_value_t * 1260 uc_fs_fdopen(uc_vm_t *vm, size_t nargs) 1261 { 1262 uc_value_t *fdno = uc_fn_arg(0); 1263 uc_value_t *mode = uc_fn_arg(1); 1264 int64_t n; 1265 FILE *fp; 1266 1267 if (ucv_type(fdno) != UC_INTEGER) 1268 err_return(EINVAL); 1269 1270 n = ucv_int64_get(fdno); 1271 1272 if (n < 0 || n > INT_MAX) 1273 err_return(EBADF); 1274 1275 fp = fdopen((int)n, 1276 ucv_type(mode) == UC_STRING ? ucv_string_get(mode) : "r"); 1277 1278 if (!fp) 1279 err_return(errno); 1280 1281 return ucv_resource_create(vm, "fs.file", fp); 1282 } 1283 1284 /** 1285 * Duplicates a file descriptor. 1286 * 1287 * This function duplicates the file descriptor `oldfd` to `newfd`. If `newfd` 1288 * was previously open, it is silently closed before being reused. 1289 * 1290 * Returns `true` on success. 1291 * Returns `null` on error. 1292 * 1293 * @function module:fs#dup2 1294 * 1295 * @param {number} oldfd 1296 * The file descriptor to duplicate. 1297 * 1298 * @param {number} newfd 1299 * The file descriptor number to duplicate to. 1300 * 1301 * @returns {?boolean} 1302 * 1303 * @example 1304 * // Redirect stderr to a log file 1305 * const logfile = open('/tmp/error.log', 'w'); 1306 * dup2(logfile.fileno(), 2); 1307 * logfile.close(); 1308 */ 1309 static uc_value_t * 1310 uc_fs_dup2(uc_vm_t *vm, size_t nargs) 1311 { 1312 uc_value_t *oldfd_arg = uc_fn_arg(0); 1313 uc_value_t *newfd_arg = uc_fn_arg(1); 1314 int oldfd, newfd; 1315 1316 oldfd = get_fd(vm, oldfd_arg); 1317 1318 if (oldfd == -1) 1319 err_return(errno ? errno : EBADF); 1320 1321 newfd = get_fd(vm, newfd_arg); 1322 1323 if (newfd == -1) 1324 err_return(errno ? errno : EBADF); 1325 1326 if (dup2(oldfd, newfd) == -1) 1327 err_return(errno); 1328 1329 return ucv_boolean_new(true); 1330 } 1331 1332 1333 /** 1334 * Represents a handle for interacting with a directory opened by `opendir()`. 1335 * 1336 * @class module:fs.dir 1337 * @hideconstructor 1338 * 1339 * @borrows module:fs#error as module:fs.dir#error 1340 * 1341 * @see {@link module:fs#opendir|opendir()} 1342 * 1343 * @example 1344 * 1345 * const handle = opendir(…); 1346 * 1347 * handle.read(); 1348 * 1349 * handle.tell(); 1350 * handle.seek(…); 1351 * 1352 * handle.close(); 1353 * 1354 * handle.error(); 1355 */ 1356 1357 /** 1358 * Obtains the number of the handle's underlying file descriptor. 1359 * 1360 * Returns the descriptor number. 1361 * 1362 * Returns `null` on error. 1363 * 1364 * @function module:fs.dir#fileno 1365 * 1366 * @returns {?number} 1367 */ 1368 static uc_value_t * 1369 uc_fs_dfileno(uc_vm_t *vm, size_t nargs) 1370 { 1371 DIR *dp = uc_fn_thisval("fs.dir"); 1372 int fd; 1373 1374 if (!dp) 1375 err_return(EBADF); 1376 1377 fd = dirfd(dp); 1378 1379 if (fd == -1) 1380 err_return(errno); 1381 1382 return ucv_int64_new(fd); 1383 } 1384 1385 /** 1386 * Read the next entry from the open directory. 1387 * 1388 * Returns a string containing the entry name. 1389 * 1390 * Returns `null` if there are no more entries to read. 1391 * 1392 * Returns `null` if an error occurred. 1393 * 1394 * @function module:fs.dir#read 1395 * 1396 * @returns {?string} 1397 */ 1398 static uc_value_t * 1399 uc_fs_readdir(uc_vm_t *vm, size_t nargs) 1400 { 1401 DIR **dp = uc_fn_this("fs.dir"); 1402 struct dirent *e; 1403 1404 if (!dp || !*dp) 1405 err_return(EINVAL); 1406 1407 errno = 0; 1408 e = readdir(*dp); 1409 1410 if (!e) 1411 err_return(errno); 1412 1413 return ucv_string_new(e->d_name); 1414 } 1415 1416 /** 1417 * Obtain current read position. 1418 * 1419 * Returns the current read position in the open directory handle which can be 1420 * passed back to the `seek()` function to return to this position. This is 1421 * mainly useful to read an open directory handle (or specific items) multiple 1422 * times. 1423 * 1424 * Returns an integer referring to the current position. 1425 * 1426 * Returns `null` if an error occurred. 1427 * 1428 * @function module:fs.dir#tell 1429 * 1430 * @returns {?number} 1431 */ 1432 static uc_value_t * 1433 uc_fs_telldir(uc_vm_t *vm, size_t nargs) 1434 { 1435 DIR **dp = uc_fn_this("fs.dir"); 1436 long position; 1437 1438 if (!dp || !*dp) 1439 err_return(EBADF); 1440 1441 position = telldir(*dp); 1442 1443 if (position == -1) 1444 err_return(errno); 1445 1446 return ucv_int64_new((int64_t)position); 1447 } 1448 1449 /** 1450 * Set read position. 1451 * 1452 * Sets the read position within the open directory handle to the given offset 1453 * value. The offset value should be obtained by a previous call to `tell()` as 1454 * the specific integer values are implementation defined. 1455 * 1456 * Returns `true` if the read position was set. 1457 * 1458 * Returns `null` if an error occurred. 1459 * 1460 * @function module:fs.dir#seek 1461 * 1462 * @param {number} offset 1463 * Position value obtained by `tell()`. 1464 * 1465 * @returns {?boolean} 1466 * 1467 * @example 1468 * 1469 * const handle = opendir("/tmp"); 1470 * const begin = handle.tell(); 1471 * 1472 * print(handle.read(), "\n"); 1473 * 1474 * handle.seek(begin); 1475 * 1476 * print(handle.read(), "\n"); // prints the first entry again 1477 */ 1478 static uc_value_t * 1479 uc_fs_seekdir(uc_vm_t *vm, size_t nargs) 1480 { 1481 uc_value_t *ofs = uc_fn_arg(0); 1482 DIR **dp = uc_fn_this("fs.dir"); 1483 long position; 1484 1485 if (ucv_type(ofs) != UC_INTEGER) 1486 err_return(EINVAL); 1487 1488 if (!dp || !*dp) 1489 err_return(EBADF); 1490 1491 position = (long)ucv_int64_get(ofs); 1492 1493 seekdir(*dp, position); 1494 1495 return ucv_boolean_new(true); 1496 } 1497 1498 /** 1499 * Closes the directory handle. 1500 * 1501 * Closes the underlying file descriptor referring to the opened directory. 1502 * 1503 * Returns `true` if the handle was properly closed. 1504 * 1505 * Returns `null` if an error occurred. 1506 * 1507 * @function module:fs.dir#close 1508 * 1509 * @returns {?boolean} 1510 */ 1511 static uc_value_t * 1512 uc_fs_closedir(uc_vm_t *vm, size_t nargs) 1513 { 1514 DIR **dp = uc_fn_this("fs.dir"); 1515 1516 if (!dp || !*dp) 1517 err_return(EBADF); 1518 1519 closedir(*dp); 1520 *dp = NULL; 1521 1522 return ucv_boolean_new(true); 1523 } 1524 1525 /** 1526 * Opens a directory and returns a directory handle associated with the open 1527 * directory descriptor. 1528 * 1529 * Returns a director handle referring to the open directory. 1530 * 1531 * Returns `null` if an error occurred. 1532 * 1533 * @function module:fs#opendir 1534 * 1535 * @param {string} path 1536 * The path to the directory. 1537 * 1538 * @returns {?module:fs.dir} 1539 * 1540 * @example 1541 * // Open a directory 1542 * const directory = opendir('path/to/directory'); 1543 */ 1544 static uc_value_t * 1545 uc_fs_opendir(uc_vm_t *vm, size_t nargs) 1546 { 1547 uc_value_t *path = uc_fn_arg(0); 1548 DIR *dp; 1549 1550 if (ucv_type(path) != UC_STRING) 1551 err_return(EINVAL); 1552 1553 dp = opendir(ucv_string_get(path)); 1554 1555 if (!dp) 1556 err_return(errno); 1557 1558 return ucv_resource_create(vm, "fs.dir", dp); 1559 } 1560 1561 /** 1562 * Reads the target path of a symbolic link. 1563 * 1564 * Returns a string containing the target path. 1565 * 1566 * Returns `null` if an error occurred. 1567 * 1568 * @function module:fs#readlink 1569 * 1570 * @param {string} path 1571 * The path to the symbolic link. 1572 * 1573 * @returns {?string} 1574 * 1575 * @example 1576 * // Read the value of a symbolic link 1577 * const targetPath = readlink('symbolicLink'); 1578 */ 1579 static uc_value_t * 1580 uc_fs_readlink(uc_vm_t *vm, size_t nargs) 1581 { 1582 uc_value_t *path = uc_fn_arg(0); 1583 uc_value_t *res; 1584 ssize_t buflen = 0, rv; 1585 char *buf = NULL, *tmp; 1586 1587 if (ucv_type(path) != UC_STRING) 1588 err_return(EINVAL); 1589 1590 do { 1591 buflen += 128; 1592 tmp = realloc(buf, buflen); 1593 1594 if (!tmp) { 1595 free(buf); 1596 err_return(ENOMEM); 1597 } 1598 1599 buf = tmp; 1600 rv = readlink(ucv_string_get(path), buf, buflen); 1601 1602 if (rv == -1) { 1603 free(buf); 1604 err_return(errno); 1605 } 1606 1607 if (rv < buflen) 1608 break; 1609 } 1610 while (true); 1611 1612 res = ucv_string_new_length(buf, rv); 1613 1614 free(buf); 1615 1616 return res; 1617 } 1618 1619 /** 1620 * @typedef {Object} module:fs.FileStatResult 1621 * @property {Object} dev - The device information. 1622 * @property {number} dev.major - The major device number. 1623 * @property {number} dev.minor - The minor device number. 1624 * @property {Object} perm - The file permissions. 1625 * @property {boolean} perm.setuid - Whether the setuid bit is set. 1626 * @property {boolean} perm.setgid - Whether the setgid bit is set. 1627 * @property {boolean} perm.sticky - Whether the sticky bit is set. 1628 * @property {boolean} perm.user_read - Whether the file is readable by the owner. 1629 * @property {boolean} perm.user_write - Whether the file is writable by the owner. 1630 * @property {boolean} perm.user_exec - Whether the file is executable by the owner. 1631 * @property {boolean} perm.group_read - Whether the file is readable by the group. 1632 * @property {boolean} perm.group_write - Whether the file is writable by the group. 1633 * @property {boolean} perm.group_exec - Whether the file is executable by the group. 1634 * @property {boolean} perm.other_read - Whether the file is readable by others. 1635 * @property {boolean} perm.other_write - Whether the file is writable by others. 1636 * @property {boolean} perm.other_exec - Whether the file is executable by others. 1637 * @property {number} inode - The inode number. 1638 * @property {number} mode - The file mode. 1639 * @property {number} nlink - The number of hard links. 1640 * @property {number} uid - The user ID of the owner. 1641 * @property {number} gid - The group ID of the owner. 1642 * @property {number} size - The file size in bytes. 1643 * @property {number} blksize - The block size for file system I/O. 1644 * @property {number} blocks - The number of 512-byte blocks allocated for the file. 1645 * @property {number} atime - The timestamp when the file was last accessed. 1646 * @property {number} mtime - The timestamp when the file was last modified. 1647 * @property {number} ctime - The timestamp when the file status was last changed. 1648 * @property {string} type - The type of the file ("directory", "file", etc.). 1649 */ 1650 1651 static uc_value_t * 1652 uc_fs_stat_common(uc_vm_t *vm, size_t nargs, bool use_lstat) 1653 { 1654 uc_value_t *path = uc_fn_arg(0); 1655 uc_value_t *res, *o; 1656 struct stat st; 1657 int rv; 1658 1659 if (ucv_type(path) != UC_STRING) 1660 err_return(EINVAL); 1661 1662 rv = (use_lstat ? lstat : stat)(ucv_string_get(path), &st); 1663 1664 if (rv == -1) 1665 err_return(errno); 1666 1667 res = ucv_object_new(vm); 1668 1669 if (!res) 1670 err_return(ENOMEM); 1671 1672 o = ucv_object_new(vm); 1673 1674 if (o) { 1675 ucv_object_add(o, "major", ucv_int64_new(major(st.st_dev))); 1676 ucv_object_add(o, "minor", ucv_int64_new(minor(st.st_dev))); 1677 1678 ucv_object_add(res, "dev", o); 1679 } 1680 1681 o = ucv_object_new(vm); 1682 1683 if (o) { 1684 ucv_object_add(o, "setuid", ucv_boolean_new(st.st_mode & S_ISUID)); 1685 ucv_object_add(o, "setgid", ucv_boolean_new(st.st_mode & S_ISGID)); 1686 ucv_object_add(o, "sticky", ucv_boolean_new(st.st_mode & S_ISVTX)); 1687 1688 ucv_object_add(o, "user_read", ucv_boolean_new(st.st_mode & S_IRUSR)); 1689 ucv_object_add(o, "user_write", ucv_boolean_new(st.st_mode & S_IWUSR)); 1690 ucv_object_add(o, "user_exec", ucv_boolean_new(st.st_mode & S_IXUSR)); 1691 1692 ucv_object_add(o, "group_read", ucv_boolean_new(st.st_mode & S_IRGRP)); 1693 ucv_object_add(o, "group_write", ucv_boolean_new(st.st_mode & S_IWGRP)); 1694 ucv_object_add(o, "group_exec", ucv_boolean_new(st.st_mode & S_IXGRP)); 1695 1696 ucv_object_add(o, "other_read", ucv_boolean_new(st.st_mode & S_IROTH)); 1697 ucv_object_add(o, "other_write", ucv_boolean_new(st.st_mode & S_IWOTH)); 1698 ucv_object_add(o, "other_exec", ucv_boolean_new(st.st_mode & S_IXOTH)); 1699 1700 ucv_object_add(res, "perm", o); 1701 } 1702 1703 ucv_object_add(res, "inode", ucv_int64_new((int64_t)st.st_ino)); 1704 ucv_object_add(res, "mode", ucv_int64_new((int64_t)st.st_mode & ~S_IFMT)); 1705 ucv_object_add(res, "nlink", ucv_int64_new((int64_t)st.st_nlink)); 1706 ucv_object_add(res, "uid", ucv_int64_new((int64_t)st.st_uid)); 1707 ucv_object_add(res, "gid", ucv_int64_new((int64_t)st.st_gid)); 1708 ucv_object_add(res, "size", ucv_int64_new((int64_t)st.st_size)); 1709 ucv_object_add(res, "blksize", ucv_int64_new((int64_t)st.st_blksize)); 1710 ucv_object_add(res, "blocks", ucv_int64_new((int64_t)st.st_blocks)); 1711 ucv_object_add(res, "atime", ucv_int64_new((int64_t)st.st_atime)); 1712 ucv_object_add(res, "mtime", ucv_int64_new((int64_t)st.st_mtime)); 1713 ucv_object_add(res, "ctime", ucv_int64_new((int64_t)st.st_ctime)); 1714 1715 if (S_ISREG(st.st_mode)) 1716 ucv_object_add(res, "type", ucv_string_new("file")); 1717 else if (S_ISDIR(st.st_mode)) 1718 ucv_object_add(res, "type", ucv_string_new("directory")); 1719 else if (S_ISCHR(st.st_mode)) 1720 ucv_object_add(res, "type", ucv_string_new("char")); 1721 else if (S_ISBLK(st.st_mode)) 1722 ucv_object_add(res, "type", ucv_string_new("block")); 1723 else if (S_ISFIFO(st.st_mode)) 1724 ucv_object_add(res, "type", ucv_string_new("fifo")); 1725 else if (S_ISLNK(st.st_mode)) 1726 ucv_object_add(res, "type", ucv_string_new("link")); 1727 else if (S_ISSOCK(st.st_mode)) 1728 ucv_object_add(res, "type", ucv_string_new("socket")); 1729 else 1730 ucv_object_add(res, "type", ucv_string_new("unknown")); 1731 1732 return res; 1733 } 1734 1735 /** 1736 * Retrieves information about a file or directory. 1737 * 1738 * Returns an object containing information about the file or directory. 1739 * 1740 * Returns `null` if an error occurred, e.g. due to insufficient permissions. 1741 * 1742 * @function module:fs#stat 1743 * 1744 * @param {string} path 1745 * The path to the file or directory. 1746 * 1747 * @returns {?module:fs.FileStatResult} 1748 * 1749 * @example 1750 * // Get information about a file 1751 * const fileInfo = stat('path/to/file'); 1752 */ 1753 static uc_value_t * 1754 uc_fs_stat(uc_vm_t *vm, size_t nargs) 1755 { 1756 return uc_fs_stat_common(vm, nargs, false); 1757 } 1758 1759 /** 1760 * Retrieves information about a file or directory, without following symbolic 1761 * links. 1762 * 1763 * Returns an object containing information about the file or directory. 1764 * 1765 * Returns `null` if an error occurred, e.g. due to insufficient permissions. 1766 * 1767 * @function module:fs#lstat 1768 * 1769 * @param {string} path 1770 * The path to the file or directory. 1771 * 1772 * @returns {?module:fs.FileStatResult} 1773 * 1774 * @example 1775 * // Get information about a directory 1776 * const dirInfo = lstat('path/to/directory'); 1777 */ 1778 static uc_value_t * 1779 uc_fs_lstat(uc_vm_t *vm, size_t nargs) 1780 { 1781 return uc_fs_stat_common(vm, nargs, true); 1782 } 1783 1784 /** 1785 * @typedef {Object} module:fs.StatVFSResult 1786 * @property {number} bsize file system block size 1787 * @property {number} frsize fragment size 1788 * @property {number} blocks total blocks 1789 * @property {number} bfree free blocks 1790 * @property {number} bavail free blocks available to unprivileged users 1791 * @property {number} files total file nodes (inodes) 1792 * @property {number} ffree free file nodes 1793 * @property {number} favail free nodes available to unprivileged users 1794 * @property {number} fsid file system id 1795 * @property {module:fs.ST_FLAGS} flag mount flags 1796 * @property {number} namemax maximum filename length 1797 * @property {number} freesize free space in bytes (calculated as `frsize * bfree`) 1798 * @property {number} totalsize total size of the filesystem (calculated as `frsize * blocks`) 1799 * @property {number} type (Linux only) magic number of the filesystem, obtained from `statfs` 1800 */ 1801 1802 /** 1803 * Query filesystem statistics for a given pathname. 1804 * 1805 * The returned object mirrors the members of `struct statvfs`. 1806 * Convenience properties `freesize` and `totalsize` are added, 1807 * which are calculated as: 1808 * - `frsize * bfree` to provide the free space in bytes and 1809 * - `frsize * blocks` to provide the total size of the filesystem. 1810 * 1811 * On Linux an additional `type` field (magic number from `statfs`) is 1812 * provided if the call succeeds. 1813 * 1814 * Returns `null` on failure (and sets `fs.last_error`). 1815 * 1816 * @function module:fs#statvfs 1817 * 1818 * @param {string} path 1819 * The path to the directory or file with which to query the filesystem. 1820 * 1821 * @returns {?module:fs.StatVFSResult} 1822 * 1823 * @example 1824 * // Get filesystem statistics for a path 1825 * const stats = statvfs('path/to/directory'); 1826 * print(stats.bsize); // file system block size 1827 */ 1828 static uc_value_t * 1829 uc_fs_statvfs(uc_vm_t *vm, size_t nargs) 1830 { 1831 uc_value_t *path = uc_fn_arg(0); 1832 struct statvfs sv; 1833 1834 if (ucv_type(path) != UC_STRING) 1835 err_return(EINVAL); 1836 1837 if (statvfs(ucv_string_get(path), &sv) == -1) 1838 err_return(errno); 1839 1840 uc_value_t *o = ucv_object_new(vm); 1841 1842 ucv_object_add(o, "bsize", ucv_int64_new(sv.f_bsize)); 1843 ucv_object_add(o, "frsize", ucv_int64_new(sv.f_frsize)); 1844 ucv_object_add(o, "blocks", ucv_int64_new(sv.f_blocks)); 1845 ucv_object_add(o, "bfree", ucv_int64_new(sv.f_bfree)); 1846 ucv_object_add(o, "bavail", ucv_int64_new(sv.f_bavail)); 1847 ucv_object_add(o, "files", ucv_int64_new(sv.f_files)); 1848 ucv_object_add(o, "ffree", ucv_int64_new(sv.f_ffree)); 1849 ucv_object_add(o, "favail", ucv_int64_new(sv.f_favail)); 1850 ucv_object_add(o, "fsid", ucv_int64_new(sv.f_fsid)); 1851 ucv_object_add(o, "flag", ucv_int64_new(sv.f_flag)); 1852 ucv_object_add(o, "namemax", ucv_int64_new(sv.f_namemax)); 1853 ucv_object_add(o, "freesize", ucv_int64_new(sv.f_frsize * sv.f_bfree)); 1854 ucv_object_add(o, "totalsize",ucv_int64_new(sv.f_frsize * sv.f_blocks)); 1855 1856 #ifdef __linux__ 1857 /* Call statfs to expose the magic number (`f_type`) */ 1858 struct statfs sf; 1859 if (statfs(ucv_string_get(path), &sf) == 0) { 1860 ucv_object_add(o, "type", ucv_int64_new(sf.f_type)); 1861 } 1862 #endif 1863 1864 return o; 1865 } 1866 1867 /** 1868 * Creates a new directory. 1869 * 1870 * Returns `true` if the directory was successfully created. 1871 * 1872 * Returns `null` if an error occurred, e.g. due to non-existent path. 1873 * 1874 * @function module:fs#mkdir 1875 * 1876 * @param {string} path 1877 * The path to the new directory. 1878 * 1879 * @returns {?boolean} 1880 * 1881 * @example 1882 * // Create a directory 1883 * mkdir('path/to/new-directory'); 1884 */ 1885 static uc_value_t * 1886 uc_fs_mkdir(uc_vm_t *vm, size_t nargs) 1887 { 1888 uc_value_t *path = uc_fn_arg(0); 1889 uc_value_t *mode = uc_fn_arg(1); 1890 1891 if (ucv_type(path) != UC_STRING || 1892 (mode && ucv_type(mode) != UC_INTEGER)) 1893 err_return(EINVAL); 1894 1895 if (mkdir(ucv_string_get(path), (mode_t)(mode ? ucv_int64_get(mode) : 0777)) == -1) 1896 err_return(errno); 1897 1898 return ucv_boolean_new(true); 1899 } 1900 1901 /** 1902 * Removes the specified directory. 1903 * 1904 * Returns `true` if the directory was successfully removed. 1905 * 1906 * Returns `null` if an error occurred, e.g. due to non-existent path. 1907 * 1908 * @function module:fs#rmdir 1909 * 1910 * @param {string} path 1911 * The path to the directory to be removed. 1912 * 1913 * @returns {?boolean} 1914 * 1915 * @example 1916 * // Remove a directory 1917 * rmdir('path/to/directory'); 1918 */ 1919 static uc_value_t * 1920 uc_fs_rmdir(uc_vm_t *vm, size_t nargs) 1921 { 1922 uc_value_t *path = uc_fn_arg(0); 1923 1924 if (ucv_type(path) != UC_STRING) 1925 err_return(EINVAL); 1926 1927 if (rmdir(ucv_string_get(path)) == -1) 1928 err_return(errno); 1929 1930 return ucv_boolean_new(true); 1931 } 1932 1933 /** 1934 * Creates a new symbolic link. 1935 * 1936 * Returns `true` if the symlink was successfully created. 1937 * 1938 * Returns `null` if an error occurred, e.g. due to non-existent path. 1939 * 1940 * @function module:fs#symlink 1941 * 1942 * @param {string} target 1943 * The target of the symbolic link. 1944 * 1945 * @param {string} path 1946 * The path of the symbolic link. 1947 * 1948 * @returns {?boolean} 1949 * 1950 * @example 1951 * // Create a symbolic link 1952 * symlink('target', 'path/to/symlink'); 1953 */ 1954 static uc_value_t * 1955 uc_fs_symlink(uc_vm_t *vm, size_t nargs) 1956 { 1957 uc_value_t *dest = uc_fn_arg(0); 1958 uc_value_t *path = uc_fn_arg(1); 1959 1960 if (ucv_type(dest) != UC_STRING || 1961 ucv_type(path) != UC_STRING) 1962 err_return(EINVAL); 1963 1964 if (symlink(ucv_string_get(dest), ucv_string_get(path)) == -1) 1965 err_return(errno); 1966 1967 return ucv_boolean_new(true); 1968 } 1969 1970 /** 1971 * Removes the specified file or symbolic link. 1972 * 1973 * Returns `true` if the unlink operation was successful. 1974 * 1975 * Returns `null` if an error occurred, e.g. due to non-existent path. 1976 * 1977 * @function module:fs#unlink 1978 * 1979 * @param {string} path 1980 * The path to the file or symbolic link. 1981 * 1982 * @returns {?boolean} 1983 * 1984 * @example 1985 * // Remove a file 1986 * unlink('path/to/file'); 1987 */ 1988 static uc_value_t * 1989 uc_fs_unlink(uc_vm_t *vm, size_t nargs) 1990 { 1991 uc_value_t *path = uc_fn_arg(0); 1992 1993 if (ucv_type(path) != UC_STRING) 1994 err_return(EINVAL); 1995 1996 if (unlink(ucv_string_get(path)) == -1) 1997 err_return(errno); 1998 1999 return ucv_boolean_new(true); 2000 } 2001 2002 /** 2003 * Retrieves the current working directory. 2004 * 2005 * Returns a string containing the current working directory path. 2006 * 2007 * Returns `null` if an error occurred. 2008 * 2009 * @function module:fs#getcwd 2010 * 2011 * @returns {?string} 2012 * 2013 * @example 2014 * // Get the current working directory 2015 * const cwd = getcwd(); 2016 */ 2017 static uc_value_t * 2018 uc_fs_getcwd(uc_vm_t *vm, size_t nargs) 2019 { 2020 uc_value_t *res; 2021 char *buf = NULL, *tmp; 2022 size_t buflen = 0; 2023 2024 do { 2025 buflen += 128; 2026 tmp = realloc(buf, buflen); 2027 2028 if (!tmp) { 2029 free(buf); 2030 err_return(ENOMEM); 2031 } 2032 2033 buf = tmp; 2034 2035 if (getcwd(buf, buflen) != NULL) 2036 break; 2037 2038 if (errno == ERANGE) 2039 continue; 2040 2041 free(buf); 2042 err_return(errno); 2043 } 2044 while (true); 2045 2046 res = ucv_string_new(buf); 2047 2048 free(buf); 2049 2050 return res; 2051 } 2052 2053 /** 2054 * Changes the current working directory to the specified path. 2055 * 2056 * Returns `true` if the permission change was successful. 2057 * 2058 * Returns `null` if an error occurred, e.g. due to insufficient permissions or 2059 * invalid arguments. 2060 * 2061 * @function module:fs#chdir 2062 * 2063 * @param {string} path 2064 * The path to the new working directory. 2065 * 2066 * @returns {?boolean} 2067 * 2068 * @example 2069 * // Change the current working directory 2070 * chdir('new-directory'); 2071 */ 2072 static uc_value_t * 2073 uc_fs_chdir(uc_vm_t *vm, size_t nargs) 2074 { 2075 uc_value_t *path = uc_fn_arg(0); 2076 2077 if (ucv_type(path) == UC_STRING) { 2078 if (chdir(ucv_string_get(path)) == -1) 2079 err_return(errno); 2080 } 2081 else { 2082 int fd = get_fd(vm, path); 2083 2084 if (fd < 0) 2085 err_return(EINVAL); 2086 2087 if (fchdir(fd) == -1) 2088 err_return(errno); 2089 } 2090 2091 return ucv_boolean_new(true); 2092 } 2093 2094 /** 2095 * Changes the permission mode bits of a file or directory. 2096 * 2097 * Returns `true` if the permission change was successful. 2098 * 2099 * Returns `null` if an error occurred, e.g. due to insufficient permissions or 2100 * invalid arguments. 2101 * 2102 * @function module:fs#chmod 2103 * 2104 * @param {string} path 2105 * The path to the file or directory. 2106 * 2107 * @param {number} mode 2108 * The new mode (permissions). 2109 * 2110 * @returns {?boolean} 2111 * 2112 * @example 2113 * // Change the mode of a file 2114 * chmod('path/to/file', 0o644); 2115 */ 2116 static uc_value_t * 2117 uc_fs_chmod(uc_vm_t *vm, size_t nargs) 2118 { 2119 uc_value_t *path = uc_fn_arg(0); 2120 uc_value_t *mode = uc_fn_arg(1); 2121 2122 if (ucv_type(path) != UC_STRING || 2123 ucv_type(mode) != UC_INTEGER) 2124 err_return(EINVAL); 2125 2126 if (chmod(ucv_string_get(path), (mode_t)ucv_int64_get(mode)) == -1) 2127 err_return(errno); 2128 2129 return ucv_boolean_new(true); 2130 } 2131 2132 static bool 2133 uc_fs_resolve_user(uc_value_t *v, uid_t *uid) 2134 { 2135 struct passwd *pw = NULL; 2136 int64_t n; 2137 char *s; 2138 2139 *uid = (uid_t)-1; 2140 2141 switch (ucv_type(v)) { 2142 case UC_INTEGER: 2143 n = ucv_int64_get(v); 2144 2145 if (n < -1) { 2146 errno = ERANGE; 2147 2148 return false; 2149 } 2150 2151 *uid = (uid_t)n; 2152 2153 return true; 2154 2155 case UC_STRING: 2156 s = ucv_string_get(v); 2157 pw = getpwnam(s); 2158 2159 if (!pw) { 2160 errno = ENOENT; 2161 2162 return false; 2163 } 2164 2165 *uid = pw->pw_uid; 2166 2167 return true; 2168 2169 case UC_NULL: 2170 return true; 2171 2172 default: 2173 errno = EINVAL; 2174 2175 return false; 2176 } 2177 } 2178 2179 static bool 2180 uc_fs_resolve_group(uc_value_t *v, gid_t *gid) 2181 { 2182 struct group *gr = NULL; 2183 int64_t n; 2184 char *s; 2185 2186 *gid = (gid_t)-1; 2187 2188 switch (ucv_type(v)) { 2189 case UC_INTEGER: 2190 n = ucv_int64_get(v); 2191 2192 if (n < -1) { 2193 errno = ERANGE; 2194 2195 return false; 2196 } 2197 2198 *gid = (gid_t)n; 2199 2200 return true; 2201 2202 case UC_STRING: 2203 s = ucv_string_get(v); 2204 gr = getgrnam(s); 2205 2206 if (!gr) { 2207 errno = ENOENT; 2208 2209 return false; 2210 } 2211 2212 *gid = gr->gr_gid; 2213 2214 return true; 2215 2216 case UC_NULL: 2217 return true; 2218 2219 default: 2220 errno = EINVAL; 2221 2222 return false; 2223 } 2224 } 2225 2226 /** 2227 * Changes the owner and group of a file or directory. 2228 * 2229 * The user and group may be specified either as uid or gid number respectively, 2230 * or as a string containing the user or group name, in which case it is 2231 * resolved to the proper uid/gid first. 2232 * 2233 * If either the user or group parameter is omitted or given as `-1`, 2234 * it is not changed. 2235 * 2236 * Returns `true` if the ownership change was successful. 2237 * 2238 * Returns `null` if an error occurred or if a user/group name cannot be 2239 * resolved to a uid/gid value. 2240 * 2241 * @function module:fs#chown 2242 * 2243 * @param {string} path 2244 * The path to the file or directory. 2245 * 2246 * @param {number|string} [uid=-1] 2247 * The new owner's user ID. When given as number, it is used as-is, when given 2248 * as string, the user name is resolved to the corresponding uid first. 2249 * 2250 * @param {number|string} [gid=-1] 2251 * The new group's ID. When given as number, it is used as-is, when given as 2252 * string, the group name is resolved to the corresponding gid first. 2253 * 2254 * @returns {?boolean} 2255 * 2256 * @example 2257 * // Change the owner of a file 2258 * chown('path/to/file', 1000); 2259 * 2260 * // Change the group of a directory 2261 * chown('/htdocs/', null, 'www-data'); 2262 */ 2263 static uc_value_t * 2264 uc_fs_chown(uc_vm_t *vm, size_t nargs) 2265 { 2266 uc_value_t *path = uc_fn_arg(0); 2267 uc_value_t *user = uc_fn_arg(1); 2268 uc_value_t *group = uc_fn_arg(2); 2269 uid_t uid; 2270 gid_t gid; 2271 2272 if (ucv_type(path) != UC_STRING) 2273 err_return(EINVAL); 2274 2275 if (!uc_fs_resolve_user(user, &uid) || 2276 !uc_fs_resolve_group(group, &gid)) 2277 err_return(errno); 2278 2279 if (chown(ucv_string_get(path), uid, gid) == -1) 2280 err_return(errno); 2281 2282 return ucv_boolean_new(true); 2283 } 2284 2285 /** 2286 * Renames or moves a file or directory. 2287 * 2288 * Returns `true` if the rename operation was successful. 2289 * 2290 * Returns `null` if an error occurred. 2291 * 2292 * @function module:fs#rename 2293 * 2294 * @param {string} oldPath 2295 * The current path of the file or directory. 2296 * 2297 * @param {string} newPath 2298 * The new path of the file or directory. 2299 * 2300 * @returns {?boolean} 2301 * 2302 * @example 2303 * // Rename a file 2304 * rename('old-name.txt', 'new-name.txt'); 2305 */ 2306 static uc_value_t * 2307 uc_fs_rename(uc_vm_t *vm, size_t nargs) 2308 { 2309 uc_value_t *oldpath = uc_fn_arg(0); 2310 uc_value_t *newpath = uc_fn_arg(1); 2311 2312 if (ucv_type(oldpath) != UC_STRING || 2313 ucv_type(newpath) != UC_STRING) 2314 err_return(EINVAL); 2315 2316 if (rename(ucv_string_get(oldpath), ucv_string_get(newpath))) 2317 err_return(errno); 2318 2319 return ucv_boolean_new(true); 2320 } 2321 2322 /** 2323 * Takes an arbitrary number of glob patterns and 2324 * resolves matching files for each one. In case of multiple patterns, 2325 * no efforts are made to remove duplicates or to globally sort the combined 2326 * match list. The list of matches for each individual pattern is sorted. 2327 * Returns an array containing all matched file paths. 2328 * 2329 * @function module:fs#glob 2330 * 2331 * @param {...Arguments} pattern 2332 * 2333 * @returns {?string[]} 2334 * 2335 * @example 2336 * 2337 * import { chdir, glob } from 'fs'; 2338 * chdir('/etc/ssl/certs/'); 2339 * for (let cert in glob('*.crt', '*.pem')) { 2340 * if (cert != null) 2341 * print(cert, '\n'); 2342 * } 2343 * // ACCVRAIZ1.crt 2344 * // AC_RAIZ_FNMT-RCM.crt 2345 * // AC_RAIZ_FNMT-RCM_SERVIDORES_SEGUROS.crt 2346 * // ... 2347 */ 2348 static uc_value_t * 2349 uc_fs_glob(uc_vm_t *vm, size_t nargs) 2350 { 2351 uc_value_t *pat, *arr; 2352 glob_t gl = { 0 }; 2353 size_t i; 2354 2355 for (i = 0; i < nargs; i++) { 2356 pat = uc_fn_arg(i); 2357 2358 if (ucv_type(pat) != UC_STRING) { 2359 globfree(&gl); 2360 err_return(EINVAL); 2361 } 2362 2363 glob(ucv_string_get(pat), i ? GLOB_APPEND : 0, NULL, &gl); 2364 } 2365 2366 arr = ucv_array_new(vm); 2367 2368 for (i = 0; i < gl.gl_pathc; i++) 2369 ucv_array_push(arr, ucv_string_new(gl.gl_pathv[i])); 2370 2371 globfree(&gl); 2372 2373 return arr; 2374 } 2375 2376 /** 2377 * Retrieves the directory name of a path. 2378 * 2379 * Returns the directory name component of the specified path. 2380 * 2381 * Returns `null` if the path argument is not a string. 2382 * 2383 * @function module:fs#dirname 2384 * 2385 * @param {string} path 2386 * The path to extract the directory name from. 2387 * 2388 * @returns {?string} 2389 * 2390 * @example 2391 * // Get the directory name of a path 2392 * const directoryName = dirname('/path/to/file.txt'); 2393 */ 2394 static uc_value_t * 2395 uc_fs_dirname(uc_vm_t *vm, size_t nargs) 2396 { 2397 uc_value_t *path = uc_fn_arg(0); 2398 size_t i; 2399 char *s; 2400 2401 if (ucv_type(path) != UC_STRING) 2402 err_return(EINVAL); 2403 2404 i = ucv_string_length(path); 2405 s = ucv_string_get(path); 2406 2407 if (i == 0) 2408 return ucv_string_new("."); 2409 2410 for (i--; s[i] == '/'; i--) 2411 if (i == 0) 2412 return ucv_string_new("/"); 2413 2414 for (; s[i] != '/'; i--) 2415 if (i == 0) 2416 return ucv_string_new("."); 2417 2418 for (; s[i] == '/'; i--) 2419 if (i == 0) 2420 return ucv_string_new("/"); 2421 2422 return ucv_string_new_length(s, i + 1); 2423 } 2424 2425 /** 2426 * Retrieves the base name of a path. 2427 * 2428 * Returns the base name component of the specified path. 2429 * 2430 * Returns `null` if the path argument is not a string. 2431 * 2432 * @function module:fs#basename 2433 * 2434 * @param {string} path 2435 * The path to extract the base name from. 2436 * 2437 * @returns {?string} 2438 * 2439 * @example 2440 * // Get the base name of a path 2441 * const baseName = basename('/path/to/file.txt'); 2442 */ 2443 static uc_value_t * 2444 uc_fs_basename(uc_vm_t *vm, size_t nargs) 2445 { 2446 uc_value_t *path = uc_fn_arg(0); 2447 size_t i, len, skip; 2448 char *s; 2449 2450 if (ucv_type(path) != UC_STRING) 2451 err_return(EINVAL); 2452 2453 len = ucv_string_length(path); 2454 s = ucv_string_get(path); 2455 2456 if (len == 0) 2457 return ucv_string_new("."); 2458 2459 for (i = len - 1, skip = 0; i > 0 && s[i] == '/'; i--, skip++) 2460 ; 2461 2462 for (; i > 0 && s[i - 1] != '/'; i--) 2463 ; 2464 2465 return ucv_string_new_length(s + i, len - i - skip); 2466 } 2467 2468 static int 2469 uc_fs_lsdir_sort_fn(const void *k1, const void *k2) 2470 { 2471 uc_value_t * const *v1 = k1; 2472 uc_value_t * const *v2 = k2; 2473 2474 return strcmp(ucv_string_get(*v1), ucv_string_get(*v2)); 2475 } 2476 2477 /** 2478 * Lists the content of a directory. 2479 * 2480 * Returns a sorted array of the names of files and directories in the specified 2481 * directory. 2482 * 2483 * Returns `null` if an error occurred, e.g. if the specified directory cannot 2484 * be opened. 2485 * 2486 * @function module:fs#lsdir 2487 * 2488 * @param {string} path 2489 * The path to the directory. 2490 * 2491 * @returns {?string[]} 2492 * 2493 * @example 2494 * // List the content of a directory 2495 * const fileList = lsdir('/path/to/directory'); 2496 */ 2497 static uc_value_t * 2498 uc_fs_lsdir(uc_vm_t *vm, size_t nargs) 2499 { 2500 uc_value_t *path = uc_fn_arg(0); 2501 uc_value_t *pat = uc_fn_arg(1); 2502 uc_value_t *res = NULL; 2503 uc_regexp_t *reg; 2504 struct dirent *e; 2505 DIR *d; 2506 2507 if (ucv_type(path) != UC_STRING) 2508 err_return(EINVAL); 2509 2510 switch (ucv_type(pat)) { 2511 case UC_NULL: 2512 case UC_STRING: 2513 case UC_REGEXP: 2514 break; 2515 2516 default: 2517 err_return(EINVAL); 2518 } 2519 2520 d = opendir(ucv_string_get(path)); 2521 2522 if (!d) 2523 err_return(errno); 2524 2525 res = ucv_array_new(vm); 2526 2527 while ((e = readdir(d)) != NULL) { 2528 if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) 2529 continue; 2530 2531 if (ucv_type(pat) == UC_REGEXP) { 2532 reg = (uc_regexp_t *)pat; 2533 2534 if (regexec(®->regexp, e->d_name, 0, NULL, 0) == REG_NOMATCH) 2535 continue; 2536 } 2537 else if (ucv_type(pat) == UC_STRING) { 2538 if (fnmatch(ucv_string_get(pat), e->d_name, 0) == FNM_NOMATCH) 2539 continue; 2540 } 2541 2542 ucv_array_push(res, ucv_string_new(e->d_name)); 2543 } 2544 2545 closedir(d); 2546 2547 ucv_array_sort(res, uc_fs_lsdir_sort_fn); 2548 2549 return res; 2550 } 2551 2552 /** 2553 * Creates a unique, ephemeral temporary file. 2554 * 2555 * Creates a new temporary file, opens it in read and write mode, unlinks it and 2556 * returns a file handle object referring to the yet open but deleted file. 2557 * 2558 * Upon closing the handle, the associated file will automatically vanish from 2559 * the system. 2560 * 2561 * The optional path template argument may be used to override the path and name 2562 * chosen for the temporary file. If the path template contains no path element, 2563 * `/tmp/` is prepended, if it does not end with `XXXXXX`, then * `.XXXXXX` is 2564 * appended to it. The `XXXXXX` sequence is replaced with a random value 2565 * ensuring uniqueness of the temporary file name. 2566 * 2567 * Returns a file handle object referring to the ephemeral file on success. 2568 * 2569 * Returns `null` if an error occurred, e.g. on insufficient permissions or 2570 * inaccessible directory. 2571 * 2572 * @function module:fs#mkstemp 2573 * 2574 * @param {string} [template="/tmp/XXXXXX"] 2575 * The path template to use when forming the temporary file name. 2576 * 2577 * @returns {?module:fs.file} 2578 * 2579 * @example 2580 * // Create a unique temporary file in the current working directory 2581 * const tempFile = mkstemp('./data-XXXXXX'); 2582 */ 2583 static uc_value_t * 2584 uc_fs_mkstemp(uc_vm_t *vm, size_t nargs) 2585 { 2586 uc_value_t *template = uc_fn_arg(0); 2587 bool ends_with_template = false; 2588 char *path, *t; 2589 FILE *fp; 2590 size_t l; 2591 int fd; 2592 2593 if (template && ucv_type(template) != UC_STRING) 2594 err_return(EINVAL); 2595 2596 t = ucv_string_get(template); 2597 l = ucv_string_length(template); 2598 2599 ends_with_template = (l >= 6 && strcmp(&t[l - 6], "XXXXXX") == 0); 2600 2601 if (t && strchr(t, '/')) { 2602 if (ends_with_template) 2603 xasprintf(&path, "%s", t); 2604 else 2605 xasprintf(&path, "%s.XXXXXX", t); 2606 } 2607 else if (t) { 2608 if (ends_with_template) 2609 xasprintf(&path, "/tmp/%s", t); 2610 else 2611 xasprintf(&path, "/tmp/%s.XXXXXX", t); 2612 } 2613 else { 2614 xasprintf(&path, "/tmp/XXXXXX"); 2615 } 2616 2617 do { 2618 fd = mkstemp(path); 2619 } 2620 while (fd == -1 && errno == EINTR); 2621 2622 if (fd == -1) { 2623 free(path); 2624 err_return(errno); 2625 } 2626 2627 unlink(path); 2628 free(path); 2629 2630 fp = fdopen(fd, "r+"); 2631 2632 if (!fp) { 2633 close(fd); 2634 err_return(errno); 2635 } 2636 2637 return ucv_resource_create(vm, "fs.file", fp); 2638 } 2639 2640 /** 2641 * Creates a unique temporary directory based on the given template. 2642 * 2643 * If the template argument is given and contains a relative path, the created 2644 * directory will be placed relative to the current working directory. 2645 * 2646 * If the template argument is given and contains an absolute path, the created 2647 * directory will be placed relative to the given directory. 2648 * 2649 * If the template argument is given but does not contain a directory separator, 2650 * the directory will be placed in `/tmp/`. 2651 * 2652 * If no template argument is given, the default `/tmp/XXXXXX` is used. 2653 * 2654 * The template argument must end with six consecutive X characters (`XXXXXX`), 2655 * which will be replaced with a random string to create the unique directory name. 2656 * If the template does not end with `XXXXXX`, it will be automatically appended. 2657 * 2658 * Returns a string containing the path of the created directory on success. 2659 * 2660 * Returns `null` if an error occurred, e.g. on insufficient permissions or 2661 * inaccessible directory. 2662 * 2663 * @function module:fs#mkdtemp 2664 * 2665 * @param {string} [template="/tmp/XXXXXX"] 2666 * The path template to use when forming the temporary directory name. 2667 * 2668 * @returns {?string} 2669 * 2670 * @example 2671 * // Create a unique temporary directory in the current working directory 2672 * const tempDir = mkdtemp('./data-XXXXXX'); 2673 */ 2674 static uc_value_t * 2675 uc_fs_mkdtemp(uc_vm_t *vm, size_t nargs) 2676 { 2677 uc_value_t *template = uc_fn_arg(0); 2678 bool ends_with_template = false; 2679 char *path, *t, *result; 2680 uc_value_t *rv; 2681 size_t l; 2682 2683 if (template && ucv_type(template) != UC_STRING) 2684 err_return(EINVAL); 2685 2686 t = ucv_string_get(template); 2687 l = ucv_string_length(template); 2688 2689 ends_with_template = (l >= 6 && strcmp(&t[l - 6], "XXXXXX") == 0); 2690 2691 if (t && strchr(t, '/')) { 2692 if (ends_with_template) 2693 xasprintf(&path, "%s", t); 2694 else 2695 xasprintf(&path, "%s.XXXXXX", t); 2696 } 2697 else if (t) { 2698 if (ends_with_template) 2699 xasprintf(&path, "/tmp/%s", t); 2700 else 2701 xasprintf(&path, "/tmp/%s.XXXXXX", t); 2702 } 2703 else { 2704 xasprintf(&path, "/tmp/XXXXXX"); 2705 } 2706 2707 result = mkdtemp(path); 2708 2709 if (!result) { 2710 free(path); 2711 err_return(errno); 2712 } 2713 2714 rv = ucv_string_new(result); 2715 free(path); 2716 2717 return rv; 2718 } 2719 2720 /** 2721 * Checks the accessibility of a file or directory. 2722 * 2723 * The optional modes argument specifies the access modes which should be 2724 * checked. A file is only considered accessible if all access modes specified 2725 * in the modes argument are possible. 2726 * 2727 * The following modes are recognized: 2728 * 2729 * | Mode | Description | 2730 * |------|---------------------------------------| 2731 * | "r" | Tests whether the file is readable. | 2732 * | "w" | Tests whether the file is writable. | 2733 * | "x" | Tests whether the file is executable. | 2734 * | "f" | Tests whether the file exists. | 2735 * 2736 * Returns `true` if the given path is accessible or `false` when it is not. 2737 * 2738 * Returns `null` if an error occurred, e.g. due to inaccessible intermediate 2739 * path components, invalid path arguments etc. 2740 * 2741 * @function module:fs#access 2742 * 2743 * @param {string} path 2744 * The path to the file or directory. 2745 * 2746 * @param {number} [mode="f"] 2747 * Optional access mode. 2748 * 2749 * @returns {?boolean} 2750 * 2751 * @example 2752 * // Check file read and write accessibility 2753 * const isAccessible = access('path/to/file', 'rw'); 2754 * 2755 * // Check execute permissions 2756 * const mayExecute = access('/usr/bin/example', 'x'); 2757 */ 2758 static uc_value_t * 2759 uc_fs_access(uc_vm_t *vm, size_t nargs) 2760 { 2761 uc_value_t *path = uc_fn_arg(0); 2762 uc_value_t *test = uc_fn_arg(1); 2763 int mode = F_OK; 2764 char *p; 2765 2766 if (ucv_type(path) != UC_STRING) 2767 err_return(EINVAL); 2768 2769 if (test && ucv_type(test) != UC_STRING) 2770 err_return(EINVAL); 2771 2772 for (p = ucv_string_get(test); p && *p; p++) { 2773 switch (*p) { 2774 case 'r': 2775 mode |= R_OK; 2776 break; 2777 2778 case 'w': 2779 mode |= W_OK; 2780 break; 2781 2782 case 'x': 2783 mode |= X_OK; 2784 break; 2785 2786 case 'f': 2787 mode |= F_OK; 2788 break; 2789 2790 default: 2791 err_return(EINVAL); 2792 } 2793 } 2794 2795 if (access(ucv_string_get(path), mode) == -1) 2796 err_return(errno); 2797 2798 return ucv_boolean_new(true); 2799 } 2800 2801 /** 2802 * Reads the content of a file, optionally limited to the given amount of bytes. 2803 * 2804 * Returns a string containing the file contents. 2805 * 2806 * Returns `null` if an error occurred, e.g. due to insufficient permissions. 2807 * 2808 * @function module:fs#readfile 2809 * 2810 * @param {string} path 2811 * The path to the file. 2812 * 2813 * @param {number} [limit] 2814 * Number of bytes to limit the result to. When omitted, the entire content is 2815 * returned. 2816 * 2817 * @returns {?string} 2818 * 2819 * @example 2820 * // Read first 100 bytes of content 2821 * const content = readfile('path/to/file', 100); 2822 * 2823 * // Read entire file content 2824 * const content = readfile('path/to/file'); 2825 */ 2826 static uc_value_t * 2827 uc_fs_readfile(uc_vm_t *vm, size_t nargs) 2828 { 2829 uc_value_t *path = uc_fn_arg(0); 2830 uc_value_t *size = uc_fn_arg(1); 2831 uc_value_t *res = NULL; 2832 uc_stringbuf_t *buf; 2833 ssize_t limit = -1; 2834 size_t rlen, blen; 2835 FILE *fp; 2836 2837 if (ucv_type(path) != UC_STRING) 2838 err_return(EINVAL); 2839 2840 if (size) { 2841 if (ucv_type(size) != UC_INTEGER) 2842 err_return(EINVAL); 2843 2844 limit = ucv_int64_get(size); 2845 } 2846 2847 fp = fopen(ucv_string_get(path), "r"); 2848 2849 if (!fp) 2850 err_return(errno); 2851 2852 buf = ucv_stringbuf_new(); 2853 2854 if (limit > -1 && limit < BUFSIZ) 2855 setvbuf(fp, NULL, _IONBF, 0); 2856 2857 while (limit != 0) { 2858 blen = 1024; 2859 2860 if (limit > 0 && blen > (size_t)limit) 2861 blen = (size_t)limit; 2862 2863 printbuf_memset(buf, printbuf_length(buf) + blen - 1, 0, 1); 2864 2865 buf->bpos -= blen; 2866 rlen = fread(buf->buf + buf->bpos, 1, blen, fp); 2867 buf->bpos += rlen; 2868 2869 if (rlen < blen) 2870 break; 2871 2872 if (limit > 0) 2873 limit -= rlen; 2874 } 2875 2876 if (ferror(fp)) { 2877 fclose(fp); 2878 printbuf_free(buf); 2879 err_return(errno); 2880 } 2881 2882 fclose(fp); 2883 2884 /* add sentinel null byte but don't count it towards the string length */ 2885 printbuf_memappend_fast(buf, "\0", 1); 2886 res = ucv_stringbuf_finish(buf); 2887 ((uc_string_t *)res)->length--; 2888 2889 return res; 2890 } 2891 2892 /** 2893 * Writes the given data to a file, optionally truncated to the given amount 2894 * of bytes. 2895 * 2896 * In case the given data is not a string, it is converted to a string before 2897 * being written into the file. String values are written as-is, integer and 2898 * double values are written in decimal notation, boolean values are written as 2899 * `true` or `false` while arrays and objects are converted to their JSON 2900 * representation before being written into the file. The `null` value is 2901 * represented by an empty string so `writefile(…, null)` would write an empty 2902 * file. Resource values are written in the form `<type address>`, e.g. 2903 * `<fs.file 0x7f60f0981760>`. 2904 * 2905 * If resource, array or object values contain a `tostring()` function in their 2906 * prototypes, then this function is invoked to obtain an alternative string 2907 * representation of the value. 2908 * 2909 * If a file already exists at the given path, it is truncated. If no file 2910 * exists, it is created with default permissions 0o666 masked by the currently 2911 * effective umask. 2912 * 2913 * Returns the number of bytes written. 2914 * 2915 * Returns `null` if an error occurred, e.g. due to insufficient permissions. 2916 * 2917 * @function module:fs#writefile 2918 * 2919 * @param {string} path 2920 * The path to the file. 2921 * 2922 * @param {*} data 2923 * The data to be written. 2924 * 2925 * @param {number} [limit] 2926 * Truncates the amount of data to be written to the specified amount of bytes. 2927 * When omitted, the entire content is written. 2928 * 2929 * @returns {?number} 2930 * 2931 * @example 2932 * // Write string to a file 2933 * const bytesWritten = writefile('path/to/file', 'Hello, World!'); 2934 * 2935 * // Write object as JSON to a file and limit to 1024 bytes at most 2936 * const obj = { foo: "Hello world", bar: true, baz: 123 }; 2937 * const bytesWritten = writefile('debug.txt', obj, 1024); 2938 */ 2939 static uc_value_t * 2940 uc_fs_writefile(uc_vm_t *vm, size_t nargs) 2941 { 2942 uc_value_t *path = uc_fn_arg(0); 2943 uc_value_t *data = uc_fn_arg(1); 2944 uc_value_t *size = uc_fn_arg(2); 2945 uc_stringbuf_t *buf = NULL; 2946 ssize_t limit = -1; 2947 size_t wlen = 0; 2948 int err = 0; 2949 FILE *fp; 2950 2951 if (ucv_type(path) != UC_STRING) 2952 err_return(EINVAL); 2953 2954 if (size) { 2955 if (ucv_type(size) != UC_INTEGER) 2956 err_return(EINVAL); 2957 2958 limit = ucv_int64_get(size); 2959 } 2960 2961 fp = fopen(ucv_string_get(path), "w"); 2962 2963 if (!fp) 2964 err_return(errno); 2965 2966 if (data && ucv_type(data) != UC_STRING) { 2967 buf = xprintbuf_new(); 2968 ucv_to_stringbuf_formatted(vm, buf, data, 0, '\0', 0); 2969 2970 if (limit < 0 || limit > printbuf_length(buf)) 2971 limit = printbuf_length(buf); 2972 2973 wlen = fwrite(buf->buf, 1, limit, fp); 2974 2975 if (wlen < (size_t)limit) 2976 err = errno; 2977 2978 printbuf_free(buf); 2979 } 2980 else if (data) { 2981 if (limit < 0 || (size_t)limit > ucv_string_length(data)) 2982 limit = ucv_string_length(data); 2983 2984 wlen = fwrite(ucv_string_get(data), 1, limit, fp); 2985 2986 if (wlen < (size_t)limit) 2987 err = errno; 2988 } 2989 2990 fclose(fp); 2991 2992 if (err) 2993 err_return(err); 2994 2995 return ucv_uint64_new(wlen); 2996 } 2997 2998 /** 2999 * Resolves the absolute path of a file or directory. 3000 * 3001 * Returns a string containing the resolved path. 3002 * 3003 * Returns `null` if an error occurred, e.g. due to insufficient permissions. 3004 * 3005 * @function module:fs#realpath 3006 * 3007 * @param {string} path 3008 * The path to the file or directory. 3009 * 3010 * @returns {?string} 3011 * 3012 * @example 3013 * // Resolve the absolute path of a file 3014 * const absolutePath = realpath('path/to/file', 'utf8'); 3015 */ 3016 static uc_value_t * 3017 uc_fs_realpath(uc_vm_t *vm, size_t nargs) 3018 { 3019 uc_value_t *path = uc_fn_arg(0), *rv; 3020 char *resolved; 3021 3022 if (ucv_type(path) != UC_STRING) 3023 err_return(EINVAL); 3024 3025 resolved = realpath(ucv_string_get(path), NULL); 3026 3027 if (!resolved) 3028 err_return(errno); 3029 3030 rv = ucv_string_new(resolved); 3031 3032 free(resolved); 3033 3034 return rv; 3035 } 3036 3037 /** 3038 * Creates a pipe and returns file handle objects associated with the read- and 3039 * write end of the pipe respectively. 3040 * 3041 * Returns a two element array containing both a file handle object open in read 3042 * mode referring to the read end of the pipe and a file handle object open in 3043 * write mode referring to the write end of the pipe. 3044 * 3045 * Returns `null` if an error occurred. 3046 * 3047 * @function module:fs#pipe 3048 * 3049 * @returns {?module:fs.file[]} 3050 * 3051 * @example 3052 * // Create a pipe 3053 * const pipeHandles = pipe(); 3054 * pipeHandles[1].write("Hello world\n"); 3055 * print(pipeHandles[0].read("line")); 3056 */ 3057 static uc_value_t * 3058 uc_fs_pipe(uc_vm_t *vm, size_t nargs) 3059 { 3060 int pfds[2], err; 3061 FILE *rfp, *wfp; 3062 uc_value_t *rv; 3063 3064 if (pipe(pfds) == -1) 3065 err_return(errno); 3066 3067 rfp = fdopen(pfds[0], "r"); 3068 3069 if (!rfp) { 3070 err = errno; 3071 close(pfds[0]); 3072 close(pfds[1]); 3073 err_return(err); 3074 } 3075 3076 wfp = fdopen(pfds[1], "w"); 3077 3078 if (!wfp) { 3079 err = errno; 3080 fclose(rfp); 3081 close(pfds[1]); 3082 err_return(err); 3083 } 3084 3085 rv = ucv_array_new_length(vm, 2); 3086 3087 ucv_array_push(rv, ucv_resource_create(vm, "fs.file", rfp)); 3088 ucv_array_push(rv, ucv_resource_create(vm, "fs.file", wfp)); 3089 3090 return rv; 3091 } 3092 3093 3094 static const uc_function_list_t proc_fns[] = { 3095 { "read", uc_fs_pread }, 3096 { "write", uc_fs_pwrite }, 3097 { "close", uc_fs_pclose }, 3098 { "flush", uc_fs_pflush }, 3099 { "fileno", uc_fs_pfileno }, 3100 { "error", uc_fs_error }, 3101 }; 3102 3103 static const uc_function_list_t file_fns[] = { 3104 { "read", uc_fs_read }, 3105 { "write", uc_fs_write }, 3106 { "seek", uc_fs_seek }, 3107 { "tell", uc_fs_tell }, 3108 { "close", uc_fs_close }, 3109 { "flush", uc_fs_flush }, 3110 { "fileno", uc_fs_fileno }, 3111 { "error", uc_fs_error }, 3112 { "isatty", uc_fs_isatty }, 3113 { "truncate", uc_fs_truncate }, 3114 { "lock", uc_fs_lock }, 3115 #if defined(__linux__) 3116 { "ioctl", uc_fs_ioctl }, 3117 #endif 3118 }; 3119 3120 static const uc_function_list_t dir_fns[] = { 3121 { "fileno", uc_fs_dfileno }, 3122 { "read", uc_fs_readdir }, 3123 { "seek", uc_fs_seekdir }, 3124 { "tell", uc_fs_telldir }, 3125 { "close", uc_fs_closedir }, 3126 { "error", uc_fs_error }, 3127 }; 3128 3129 static const uc_function_list_t global_fns[] = { 3130 { "error", uc_fs_error }, 3131 { "open", uc_fs_open }, 3132 { "fdopen", uc_fs_fdopen }, 3133 { "dup2", uc_fs_dup2 }, 3134 { "opendir", uc_fs_opendir }, 3135 { "popen", uc_fs_popen }, 3136 { "readlink", uc_fs_readlink }, 3137 { "stat", uc_fs_stat }, 3138 { "statvfs", uc_fs_statvfs }, 3139 { "lstat", uc_fs_lstat }, 3140 { "mkdir", uc_fs_mkdir }, 3141 { "rmdir", uc_fs_rmdir }, 3142 { "symlink", uc_fs_symlink }, 3143 { "unlink", uc_fs_unlink }, 3144 { "getcwd", uc_fs_getcwd }, 3145 { "chdir", uc_fs_chdir }, 3146 { "chmod", uc_fs_chmod }, 3147 { "chown", uc_fs_chown }, 3148 { "rename", uc_fs_rename }, 3149 { "glob", uc_fs_glob }, 3150 { "dirname", uc_fs_dirname }, 3151 { "basename", uc_fs_basename }, 3152 { "lsdir", uc_fs_lsdir }, 3153 { "mkstemp", uc_fs_mkstemp }, 3154 { "mkdtemp", uc_fs_mkdtemp }, 3155 { "access", uc_fs_access }, 3156 { "readfile", uc_fs_readfile }, 3157 { "writefile", uc_fs_writefile }, 3158 { "realpath", uc_fs_realpath }, 3159 { "pipe", uc_fs_pipe }, 3160 }; 3161 3162 3163 static void close_proc(void *ud) 3164 { 3165 FILE *fp = ud; 3166 3167 if (fp) 3168 pclose(fp); 3169 } 3170 3171 static void close_file(void *ud) 3172 { 3173 FILE *fp = ud; 3174 int n; 3175 3176 n = fp ? fileno(fp) : -1; 3177 3178 if (n > 2) 3179 fclose(fp); 3180 } 3181 3182 static void close_dir(void *ud) 3183 { 3184 DIR *dp = ud; 3185 3186 if (dp) 3187 closedir(dp); 3188 } 3189 3190 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 3191 { 3192 uc_function_list_register(scope, global_fns); 3193 #define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x)) 3194 3195 /** 3196 * @typedef 3197 * @name module:fs.ST_FLAGS 3198 * @description Bitmask flags used at volume mount time (¹ - Linux only). 3199 * @property {number} ST_MANDLOCK - Mandatory locking.¹ 3200 * @property {number} ST_NOATIME - Do not update access times.¹ 3201 * @property {number} ST_NODEV - Do not allow device files.¹ 3202 * @property {number} ST_NODIRATIME - Do not update directory access times.¹ 3203 * @property {number} ST_NOEXEC - Do not allow execution of binaries.¹ 3204 * @property {number} ST_NOSUID - Do not allow set-user-identifier or set-group-identifier bits. 3205 * @property {number} ST_RDONLY - Read-only filesystem. 3206 * @property {number} ST_RELATIME - Update access times relative to modification time.¹ 3207 * @property {number} ST_SYNCHRONOUS - Synchronous writes.¹ 3208 * @property {number} ST_NOSYMFOLLOW - Do not follow symbolic links.¹ 3209 */ 3210 #ifdef ST_MANDLOCK 3211 ADD_CONST(ST_MANDLOCK); 3212 #endif 3213 #ifdef ST_NOATIME 3214 ADD_CONST(ST_NOATIME); 3215 #endif 3216 #ifdef ST_NODEV 3217 ADD_CONST(ST_NODEV); 3218 #endif 3219 #ifdef ST_NODIRATIME 3220 ADD_CONST(ST_NODIRATIME); 3221 #endif 3222 #ifdef ST_NOEXEC 3223 ADD_CONST(ST_NOEXEC); 3224 #endif 3225 #ifdef ST_NOSUID 3226 ADD_CONST(ST_NOSUID); 3227 #endif 3228 #ifdef ST_RDONLY 3229 ADD_CONST(ST_RDONLY); 3230 #endif 3231 #ifdef ST_RELATIME 3232 ADD_CONST(ST_RELATIME); 3233 #endif 3234 #ifdef ST_SYNCHRONOUS 3235 ADD_CONST(ST_SYNCHRONOUS); 3236 #endif 3237 #ifdef ST_NOSYMFOLLOW 3238 ADD_CONST(ST_NOSYMFOLLOW); 3239 #endif 3240 3241 uc_type_declare(vm, "fs.proc", proc_fns, close_proc); 3242 uc_type_declare(vm, "fs.dir", dir_fns, close_dir); 3243 3244 uc_resource_type_t *file_type = uc_type_declare(vm, "fs.file", file_fns, close_file); 3245 3246 ucv_object_add(scope, "stdin", uc_resource_new(file_type, stdin)); 3247 ucv_object_add(scope, "stdout", uc_resource_new(file_type, stdout)); 3248 ucv_object_add(scope, "stderr", uc_resource_new(file_type, stderr)); 3249 3250 #ifdef HAS_IOCTL 3251 ADD_CONST(IOC_DIR_NONE); 3252 ADD_CONST(IOC_DIR_READ); 3253 ADD_CONST(IOC_DIR_WRITE); 3254 ADD_CONST(IOC_DIR_RW); 3255 #endif 3256 } 3257
This page was automatically generated by LXR 0.3.1. • OpenWrt