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