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