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