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