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