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