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