1'use strict'; 2 3const { 4 ArrayPrototypePush, 5 ArrayPrototypePop, 6 Error, 7 MathMax, 8 MathMin, 9 NumberIsSafeInteger, 10 Promise, 11 PromisePrototypeThen, 12 PromiseResolve, 13 PromiseReject, 14 SafeArrayIterator, 15 SafePromisePrototypeFinally, 16 Symbol, 17 SymbolAsyncDispose, 18 Uint8Array, 19 FunctionPrototypeBind, 20} = primordials; 21 22const { fs: constants } = internalBinding('constants'); 23const { 24 F_OK, 25 O_SYMLINK, 26 O_WRONLY, 27 S_IFMT, 28 S_IFREG, 29} = constants; 30 31const binding = internalBinding('fs'); 32const { Buffer } = require('buffer'); 33 34const { 35 codes: { 36 ERR_FS_FILE_TOO_LARGE, 37 ERR_INVALID_ARG_VALUE, 38 ERR_INVALID_STATE, 39 ERR_METHOD_NOT_IMPLEMENTED, 40 }, 41 AbortError, 42 aggregateTwoErrors, 43} = require('internal/errors'); 44const { isArrayBufferView } = require('internal/util/types'); 45const { rimrafPromises } = require('internal/fs/rimraf'); 46const { 47 constants: { 48 kIoMaxLength, 49 kMaxUserId, 50 kReadFileBufferLength, 51 kReadFileUnknownBufferLength, 52 kWriteFileMaxChunkSize, 53 }, 54 copyObject, 55 emitRecursiveRmdirWarning, 56 getDirents, 57 getOptions, 58 getStatFsFromBinding, 59 getStatsFromBinding, 60 getValidatedPath, 61 getValidMode, 62 nullCheck, 63 preprocessSymlinkDestination, 64 stringToFlags, 65 stringToSymlinkType, 66 toUnixTimestamp, 67 validateBufferArray, 68 validateCpOptions, 69 validateOffsetLengthRead, 70 validateOffsetLengthWrite, 71 validateRmOptions, 72 validateRmdirOptions, 73 validatePrimitiveStringAfterArrayBufferView, 74 warnOnNonPortableTemplate, 75} = require('internal/fs/utils'); 76const { opendir } = require('internal/fs/dir'); 77const { 78 parseFileMode, 79 validateAbortSignal, 80 validateBoolean, 81 validateBuffer, 82 validateEncoding, 83 validateInteger, 84 validateString, 85} = require('internal/validators'); 86const pathModule = require('path'); 87const { 88 kEmptyObject, 89 lazyDOMException, 90 promisify, 91} = require('internal/util'); 92const { EventEmitterMixin } = require('internal/event_target'); 93const { StringDecoder } = require('string_decoder'); 94const { watch } = require('internal/fs/watchers'); 95const { isIterable } = require('internal/streams/utils'); 96const assert = require('internal/assert'); 97 98const kHandle = Symbol('kHandle'); 99const kFd = Symbol('kFd'); 100const kRefs = Symbol('kRefs'); 101const kClosePromise = Symbol('kClosePromise'); 102const kCloseResolve = Symbol('kCloseResolve'); 103const kCloseReject = Symbol('kCloseReject'); 104const kRef = Symbol('kRef'); 105const kUnref = Symbol('kUnref'); 106const kLocked = Symbol('kLocked'); 107 108const { kUsePromises } = binding; 109const { Interface } = require('internal/readline/interface'); 110const { 111 JSTransferable, kDeserialize, kTransfer, kTransferList, 112} = require('internal/worker/js_transferable'); 113 114const getDirectoryEntriesPromise = promisify(getDirents); 115const validateRmOptionsPromise = promisify(validateRmOptions); 116 117let cpPromises; 118function lazyLoadCpPromises() { 119 return cpPromises ??= require('internal/fs/cp/cp').cpFn; 120} 121 122// Lazy loaded to avoid circular dependency. 123let fsStreams; 124function lazyFsStreams() { 125 return fsStreams ??= require('internal/fs/streams'); 126} 127 128class FileHandle extends EventEmitterMixin(JSTransferable) { 129 /** 130 * @param {InternalFSBinding.FileHandle | undefined} filehandle 131 */ 132 constructor(filehandle) { 133 super(); 134 this[kHandle] = filehandle; 135 this[kFd] = filehandle ? filehandle.fd : -1; 136 137 this[kRefs] = 1; 138 this[kClosePromise] = null; 139 } 140 141 getAsyncId() { 142 return this[kHandle].getAsyncId(); 143 } 144 145 get fd() { 146 return this[kFd]; 147 } 148 149 appendFile(data, options) { 150 return fsCall(writeFile, this, data, options); 151 } 152 153 chmod(mode) { 154 return fsCall(fchmod, this, mode); 155 } 156 157 chown(uid, gid) { 158 return fsCall(fchown, this, uid, gid); 159 } 160 161 datasync() { 162 return fsCall(fdatasync, this); 163 } 164 165 sync() { 166 return fsCall(fsync, this); 167 } 168 169 read(buffer, offset, length, position) { 170 return fsCall(read, this, buffer, offset, length, position); 171 } 172 173 readv(buffers, position) { 174 return fsCall(readv, this, buffers, position); 175 } 176 177 readFile(options) { 178 return fsCall(readFile, this, options); 179 } 180 181 readLines(options = undefined) { 182 return new Interface({ 183 input: this.createReadStream(options), 184 crlfDelay: Infinity, 185 }); 186 } 187 188 stat(options) { 189 return fsCall(fstat, this, options); 190 } 191 192 truncate(len = 0) { 193 return fsCall(ftruncate, this, len); 194 } 195 196 utimes(atime, mtime) { 197 return fsCall(futimes, this, atime, mtime); 198 } 199 200 write(buffer, offset, length, position) { 201 return fsCall(write, this, buffer, offset, length, position); 202 } 203 204 writev(buffers, position) { 205 return fsCall(writev, this, buffers, position); 206 } 207 208 writeFile(data, options) { 209 return fsCall(writeFile, this, data, options); 210 } 211 212 close = () => { 213 if (this[kFd] === -1) { 214 return PromiseResolve(); 215 } 216 217 if (this[kClosePromise]) { 218 return this[kClosePromise]; 219 } 220 221 this[kRefs]--; 222 if (this[kRefs] === 0) { 223 this[kFd] = -1; 224 this[kClosePromise] = SafePromisePrototypeFinally( 225 this[kHandle].close(), 226 () => { this[kClosePromise] = undefined; }, 227 ); 228 } else { 229 this[kClosePromise] = SafePromisePrototypeFinally( 230 new Promise((resolve, reject) => { 231 this[kCloseResolve] = resolve; 232 this[kCloseReject] = reject; 233 }), () => { 234 this[kClosePromise] = undefined; 235 this[kCloseReject] = undefined; 236 this[kCloseResolve] = undefined; 237 }, 238 ); 239 } 240 241 this.emit('close'); 242 return this[kClosePromise]; 243 }; 244 245 async [SymbolAsyncDispose]() { 246 return this.close(); 247 } 248 249 /** 250 * @typedef {import('../webstreams/readablestream').ReadableStream 251 * } ReadableStream 252 * @returns {ReadableStream} 253 */ 254 readableWebStream(options = kEmptyObject) { 255 if (this[kFd] === -1) 256 throw new ERR_INVALID_STATE('The FileHandle is closed'); 257 if (this[kClosePromise]) 258 throw new ERR_INVALID_STATE('The FileHandle is closing'); 259 if (this[kLocked]) 260 throw new ERR_INVALID_STATE('The FileHandle is locked'); 261 this[kLocked] = true; 262 263 if (options.type !== undefined) { 264 validateString(options.type, 'options.type'); 265 } 266 267 let readable; 268 269 if (options.type !== 'bytes') { 270 const { 271 newReadableStreamFromStreamBase, 272 } = require('internal/webstreams/adapters'); 273 readable = newReadableStreamFromStreamBase( 274 this[kHandle], 275 undefined, 276 { ondone: () => this[kUnref]() }); 277 278 const { 279 readableStreamCancel, 280 } = require('internal/webstreams/readablestream'); 281 this[kRef](); 282 this.once('close', () => { 283 readableStreamCancel(readable); 284 }); 285 } else { 286 const { 287 readableStreamCancel, 288 ReadableStream, 289 } = require('internal/webstreams/readablestream'); 290 291 const readFn = FunctionPrototypeBind(this.read, this); 292 const ondone = FunctionPrototypeBind(this[kUnref], this); 293 294 readable = new ReadableStream({ 295 type: 'bytes', 296 autoAllocateChunkSize: 16384, 297 298 async pull(controller) { 299 const view = controller.byobRequest.view; 300 const { bytesRead } = await readFn(view, view.byteOffset, view.byteLength); 301 302 if (bytesRead === 0) { 303 ondone(); 304 controller.close(); 305 } 306 307 controller.byobRequest.respond(bytesRead); 308 }, 309 310 cancel() { 311 ondone(); 312 }, 313 }); 314 315 this[kRef](); 316 317 this.once('close', () => { 318 readableStreamCancel(readable); 319 }); 320 } 321 322 return readable; 323 } 324 325 /** 326 * @typedef {import('./streams').ReadStream 327 * } ReadStream 328 * @param {{ 329 * encoding?: string; 330 * autoClose?: boolean; 331 * emitClose?: boolean; 332 * start: number; 333 * end?: number; 334 * highWaterMark?: number; 335 * }} [options] 336 * @returns {ReadStream} 337 */ 338 createReadStream(options = undefined) { 339 const { ReadStream } = lazyFsStreams(); 340 return new ReadStream(undefined, { ...options, fd: this }); 341 } 342 343 /** 344 * @typedef {import('./streams').WriteStream 345 * } WriteStream 346 * @param {{ 347 * encoding?: string; 348 * autoClose?: boolean; 349 * emitClose?: boolean; 350 * start: number; 351 * }} [options] 352 * @returns {WriteStream} 353 */ 354 createWriteStream(options = undefined) { 355 const { WriteStream } = lazyFsStreams(); 356 return new WriteStream(undefined, { ...options, fd: this }); 357 } 358 359 [kTransfer]() { 360 if (this[kClosePromise] || this[kRefs] > 1) { 361 throw lazyDOMException('Cannot transfer FileHandle while in use', 362 'DataCloneError'); 363 } 364 365 const handle = this[kHandle]; 366 this[kFd] = -1; 367 this[kHandle] = null; 368 this[kRefs] = 0; 369 370 return { 371 data: { handle }, 372 deserializeInfo: 'internal/fs/promises:FileHandle', 373 }; 374 } 375 376 [kTransferList]() { 377 return [ this[kHandle] ]; 378 } 379 380 [kDeserialize]({ handle }) { 381 this[kHandle] = handle; 382 this[kFd] = handle.fd; 383 } 384 385 [kRef]() { 386 this[kRefs]++; 387 } 388 389 [kUnref]() { 390 this[kRefs]--; 391 if (this[kRefs] === 0) { 392 this[kFd] = -1; 393 PromisePrototypeThen( 394 this[kHandle].close(), 395 this[kCloseResolve], 396 this[kCloseReject], 397 ); 398 } 399 } 400} 401 402async function handleFdClose(fileOpPromise, closeFunc) { 403 return PromisePrototypeThen( 404 fileOpPromise, 405 (result) => PromisePrototypeThen(closeFunc(), () => result), 406 (opError) => 407 PromisePrototypeThen( 408 closeFunc(), 409 () => PromiseReject(opError), 410 (closeError) => PromiseReject(aggregateTwoErrors(closeError, opError)), 411 ), 412 ); 413} 414 415async function fsCall(fn, handle, ...args) { 416 assert(handle[kRefs] !== undefined, 417 'handle must be an instance of FileHandle'); 418 419 if (handle.fd === -1) { 420 // eslint-disable-next-line no-restricted-syntax 421 const err = new Error('file closed'); 422 err.code = 'EBADF'; 423 err.syscall = fn.name; 424 throw err; 425 } 426 427 try { 428 handle[kRef](); 429 return await fn(handle, ...new SafeArrayIterator(args)); 430 } finally { 431 handle[kUnref](); 432 } 433} 434 435function checkAborted(signal) { 436 if (signal?.aborted) 437 throw new AbortError(undefined, { cause: signal?.reason }); 438} 439 440async function writeFileHandle(filehandle, data, signal, encoding) { 441 checkAborted(signal); 442 if (isCustomIterable(data)) { 443 for await (const buf of data) { 444 checkAborted(signal); 445 const toWrite = 446 isArrayBufferView(buf) ? buf : Buffer.from(buf, encoding || 'utf8'); 447 let remaining = toWrite.byteLength; 448 while (remaining > 0) { 449 const writeSize = MathMin(kWriteFileMaxChunkSize, remaining); 450 const { bytesWritten } = await write( 451 filehandle, toWrite, toWrite.byteLength - remaining, writeSize); 452 remaining -= bytesWritten; 453 checkAborted(signal); 454 } 455 } 456 return; 457 } 458 data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength); 459 let remaining = data.byteLength; 460 if (remaining === 0) return; 461 do { 462 checkAborted(signal); 463 const { bytesWritten } = 464 await write(filehandle, data, 0, 465 MathMin(kWriteFileMaxChunkSize, data.byteLength)); 466 remaining -= bytesWritten; 467 data = new Uint8Array( 468 data.buffer, 469 data.byteOffset + bytesWritten, 470 data.byteLength - bytesWritten, 471 ); 472 } while (remaining > 0); 473} 474 475async function readFileHandle(filehandle, options) { 476 const signal = options?.signal; 477 const encoding = options?.encoding; 478 const decoder = encoding && new StringDecoder(encoding); 479 480 checkAborted(signal); 481 482 const statFields = await binding.fstat(filehandle.fd, false, kUsePromises); 483 484 checkAborted(signal); 485 486 let size = 0; 487 let length = 0; 488 if ((statFields[1/* mode */] & S_IFMT) === S_IFREG) { 489 size = statFields[8/* size */]; 490 length = encoding ? MathMin(size, kReadFileBufferLength) : size; 491 } 492 if (length === 0) { 493 length = kReadFileUnknownBufferLength; 494 } 495 496 if (size > kIoMaxLength) 497 throw new ERR_FS_FILE_TOO_LARGE(size); 498 499 let totalRead = 0; 500 let buffer = Buffer.allocUnsafeSlow(length); 501 let result = ''; 502 let offset = 0; 503 let buffers; 504 const chunkedRead = length > kReadFileBufferLength; 505 506 while (true) { 507 checkAborted(signal); 508 509 if (chunkedRead) { 510 length = MathMin(size - totalRead, kReadFileBufferLength); 511 } 512 513 const bytesRead = (await binding.read(filehandle.fd, buffer, offset, 514 length, -1, kUsePromises)) ?? 0; 515 totalRead += bytesRead; 516 517 if (bytesRead === 0 || 518 totalRead === size || 519 (bytesRead !== buffer.length && !chunkedRead)) { 520 const singleRead = bytesRead === totalRead; 521 522 const bytesToCheck = chunkedRead ? totalRead : bytesRead; 523 524 if (bytesToCheck !== buffer.length) { 525 buffer = buffer.subarray(0, bytesToCheck); 526 } 527 528 if (!encoding) { 529 if (size === 0 && !singleRead) { 530 ArrayPrototypePush(buffers, buffer); 531 return Buffer.concat(buffers, totalRead); 532 } 533 return buffer; 534 } 535 536 if (singleRead) { 537 return buffer.toString(encoding); 538 } 539 result += decoder.end(buffer); 540 return result; 541 } 542 543 if (encoding) { 544 result += decoder.write(buffer); 545 } else if (size !== 0) { 546 offset = totalRead; 547 } else { 548 buffers ??= []; 549 // Unknown file size requires chunks. 550 ArrayPrototypePush(buffers, buffer); 551 buffer = Buffer.allocUnsafeSlow(kReadFileUnknownBufferLength); 552 } 553 } 554} 555 556// All of the functions are defined as async in order to ensure that errors 557// thrown cause promise rejections rather than being thrown synchronously. 558async function access(path, mode = F_OK) { 559 path = getValidatedPath(path); 560 561 mode = getValidMode(mode, 'access'); 562 return binding.access(pathModule.toNamespacedPath(path), mode, 563 kUsePromises); 564} 565 566async function cp(src, dest, options) { 567 options = validateCpOptions(options); 568 src = pathModule.toNamespacedPath(getValidatedPath(src, 'src')); 569 dest = pathModule.toNamespacedPath(getValidatedPath(dest, 'dest')); 570 return lazyLoadCpPromises()(src, dest, options); 571} 572 573async function copyFile(src, dest, mode) { 574 src = getValidatedPath(src, 'src'); 575 dest = getValidatedPath(dest, 'dest'); 576 mode = getValidMode(mode, 'copyFile'); 577 return binding.copyFile(pathModule.toNamespacedPath(src), 578 pathModule.toNamespacedPath(dest), 579 mode, 580 kUsePromises); 581} 582 583// Note that unlike fs.open() which uses numeric file descriptors, 584// fsPromises.open() uses the fs.FileHandle class. 585async function open(path, flags, mode) { 586 path = getValidatedPath(path); 587 const flagsNumber = stringToFlags(flags); 588 mode = parseFileMode(mode, 'mode', 0o666); 589 return new FileHandle( 590 await binding.openFileHandle(pathModule.toNamespacedPath(path), 591 flagsNumber, mode, kUsePromises)); 592} 593 594async function read(handle, bufferOrParams, offset, length, position) { 595 let buffer = bufferOrParams; 596 if (!isArrayBufferView(buffer)) { 597 // This is fh.read(params) 598 ({ 599 buffer = Buffer.alloc(16384), 600 offset = 0, 601 length = buffer.byteLength - offset, 602 position = null, 603 } = bufferOrParams ?? kEmptyObject); 604 605 validateBuffer(buffer); 606 } 607 608 if (offset !== null && typeof offset === 'object') { 609 // This is fh.read(buffer, options) 610 ({ 611 offset = 0, 612 length = buffer.byteLength - offset, 613 position = null, 614 } = offset); 615 } 616 617 if (offset == null) { 618 offset = 0; 619 } else { 620 validateInteger(offset, 'offset', 0); 621 } 622 623 length |= 0; 624 625 if (length === 0) 626 return { bytesRead: length, buffer }; 627 628 if (buffer.byteLength === 0) { 629 throw new ERR_INVALID_ARG_VALUE('buffer', buffer, 630 'is empty and cannot be written'); 631 } 632 633 validateOffsetLengthRead(offset, length, buffer.byteLength); 634 635 if (!NumberIsSafeInteger(position)) 636 position = -1; 637 638 const bytesRead = (await binding.read(handle.fd, buffer, offset, length, 639 position, kUsePromises)) || 0; 640 641 return { bytesRead, buffer }; 642} 643 644async function readv(handle, buffers, position) { 645 validateBufferArray(buffers); 646 647 if (typeof position !== 'number') 648 position = null; 649 650 const bytesRead = (await binding.readBuffers(handle.fd, buffers, position, 651 kUsePromises)) || 0; 652 return { bytesRead, buffers }; 653} 654 655async function write(handle, buffer, offsetOrOptions, length, position) { 656 if (buffer?.byteLength === 0) 657 return { bytesWritten: 0, buffer }; 658 659 let offset = offsetOrOptions; 660 if (isArrayBufferView(buffer)) { 661 if (typeof offset === 'object') { 662 ({ 663 offset = 0, 664 length = buffer.byteLength - offset, 665 position = null, 666 } = offsetOrOptions ?? kEmptyObject); 667 } 668 669 if (offset == null) { 670 offset = 0; 671 } else { 672 validateInteger(offset, 'offset', 0); 673 } 674 if (typeof length !== 'number') 675 length = buffer.byteLength - offset; 676 if (typeof position !== 'number') 677 position = null; 678 validateOffsetLengthWrite(offset, length, buffer.byteLength); 679 const bytesWritten = 680 (await binding.writeBuffer(handle.fd, buffer, offset, 681 length, position, kUsePromises)) || 0; 682 return { bytesWritten, buffer }; 683 } 684 685 validatePrimitiveStringAfterArrayBufferView(buffer, 'buffer'); 686 validateEncoding(buffer, length); 687 const bytesWritten = (await binding.writeString(handle.fd, buffer, offset, 688 length, kUsePromises)) || 0; 689 return { bytesWritten, buffer }; 690} 691 692async function writev(handle, buffers, position) { 693 validateBufferArray(buffers); 694 695 if (typeof position !== 'number') 696 position = null; 697 698 if (buffers.length === 0) { 699 return { bytesWritten: 0, buffers }; 700 } 701 702 const bytesWritten = (await binding.writeBuffers(handle.fd, buffers, position, 703 kUsePromises)) || 0; 704 return { bytesWritten, buffers }; 705} 706 707async function rename(oldPath, newPath) { 708 oldPath = getValidatedPath(oldPath, 'oldPath'); 709 newPath = getValidatedPath(newPath, 'newPath'); 710 return binding.rename(pathModule.toNamespacedPath(oldPath), 711 pathModule.toNamespacedPath(newPath), 712 kUsePromises); 713} 714 715async function truncate(path, len = 0) { 716 const fd = await open(path, 'r+'); 717 return handleFdClose(ftruncate(fd, len), fd.close); 718} 719 720async function ftruncate(handle, len = 0) { 721 validateInteger(len, 'len'); 722 len = MathMax(0, len); 723 return binding.ftruncate(handle.fd, len, kUsePromises); 724} 725 726async function rm(path, options) { 727 path = pathModule.toNamespacedPath(getValidatedPath(path)); 728 options = await validateRmOptionsPromise(path, options, false); 729 return rimrafPromises(path, options); 730} 731 732async function rmdir(path, options) { 733 path = pathModule.toNamespacedPath(getValidatedPath(path)); 734 options = validateRmdirOptions(options); 735 736 if (options.recursive) { 737 emitRecursiveRmdirWarning(); 738 const stats = await stat(path); 739 if (stats.isDirectory()) { 740 return rimrafPromises(path, options); 741 } 742 } 743 744 return binding.rmdir(path, kUsePromises); 745} 746 747async function fdatasync(handle) { 748 return binding.fdatasync(handle.fd, kUsePromises); 749} 750 751async function fsync(handle) { 752 return binding.fsync(handle.fd, kUsePromises); 753} 754 755async function mkdir(path, options) { 756 if (typeof options === 'number' || typeof options === 'string') { 757 options = { mode: options }; 758 } 759 const { 760 recursive = false, 761 mode = 0o777, 762 } = options || kEmptyObject; 763 path = getValidatedPath(path); 764 validateBoolean(recursive, 'options.recursive'); 765 766 return binding.mkdir(pathModule.toNamespacedPath(path), 767 parseFileMode(mode, 'mode', 0o777), recursive, 768 kUsePromises); 769} 770 771async function readdirRecursive(originalPath, options) { 772 const result = []; 773 const queue = [ 774 [ 775 originalPath, 776 await binding.readdir( 777 pathModule.toNamespacedPath(originalPath), 778 options.encoding, 779 !!options.withFileTypes, 780 kUsePromises, 781 ), 782 ], 783 ]; 784 785 786 if (options.withFileTypes) { 787 while (queue.length > 0) { 788 // If we want to implement BFS make this a `shift` call instead of `pop` 789 const { 0: path, 1: readdir } = ArrayPrototypePop(queue); 790 for (const dirent of getDirents(path, readdir)) { 791 ArrayPrototypePush(result, dirent); 792 if (dirent.isDirectory()) { 793 const direntPath = pathModule.join(path, dirent.name); 794 ArrayPrototypePush(queue, [ 795 direntPath, 796 await binding.readdir( 797 direntPath, 798 options.encoding, 799 true, 800 kUsePromises, 801 ), 802 ]); 803 } 804 } 805 } 806 } else { 807 while (queue.length > 0) { 808 const { 0: path, 1: readdir } = ArrayPrototypePop(queue); 809 for (const ent of readdir) { 810 const direntPath = pathModule.join(path, ent); 811 const stat = binding.internalModuleStat(direntPath); 812 ArrayPrototypePush( 813 result, 814 pathModule.relative(originalPath, direntPath), 815 ); 816 if (stat === 1) { 817 ArrayPrototypePush(queue, [ 818 direntPath, 819 await binding.readdir( 820 pathModule.toNamespacedPath(direntPath), 821 options.encoding, 822 false, 823 kUsePromises, 824 ), 825 ]); 826 } 827 } 828 } 829 } 830 831 return result; 832} 833 834async function readdir(path, options) { 835 options = getOptions(options); 836 path = getValidatedPath(path); 837 if (options.recursive) { 838 return readdirRecursive(path, options); 839 } 840 const result = await binding.readdir( 841 pathModule.toNamespacedPath(path), 842 options.encoding, 843 !!options.withFileTypes, 844 kUsePromises, 845 ); 846 return options.withFileTypes ? 847 getDirectoryEntriesPromise(path, result) : 848 result; 849} 850 851async function readlink(path, options) { 852 options = getOptions(options); 853 path = getValidatedPath(path, 'oldPath'); 854 return binding.readlink(pathModule.toNamespacedPath(path), 855 options.encoding, kUsePromises); 856} 857 858async function symlink(target, path, type_) { 859 const type = (typeof type_ === 'string' ? type_ : null); 860 target = getValidatedPath(target, 'target'); 861 path = getValidatedPath(path); 862 return binding.symlink(preprocessSymlinkDestination(target, type, path), 863 pathModule.toNamespacedPath(path), 864 stringToSymlinkType(type), 865 kUsePromises); 866} 867 868async function fstat(handle, options = { bigint: false }) { 869 const result = await binding.fstat(handle.fd, options.bigint, kUsePromises); 870 return getStatsFromBinding(result); 871} 872 873async function lstat(path, options = { bigint: false }) { 874 path = getValidatedPath(path); 875 const result = await binding.lstat(pathModule.toNamespacedPath(path), 876 options.bigint, kUsePromises); 877 return getStatsFromBinding(result); 878} 879 880async function stat(path, options = { bigint: false }) { 881 path = getValidatedPath(path); 882 const result = await binding.stat(pathModule.toNamespacedPath(path), 883 options.bigint, kUsePromises); 884 return getStatsFromBinding(result); 885} 886 887async function statfs(path, options = { bigint: false }) { 888 path = getValidatedPath(path); 889 const result = await binding.statfs(pathModule.toNamespacedPath(path), 890 options.bigint, kUsePromises); 891 return getStatFsFromBinding(result); 892} 893 894async function link(existingPath, newPath) { 895 existingPath = getValidatedPath(existingPath, 'existingPath'); 896 newPath = getValidatedPath(newPath, 'newPath'); 897 return binding.link(pathModule.toNamespacedPath(existingPath), 898 pathModule.toNamespacedPath(newPath), 899 kUsePromises); 900} 901 902async function unlink(path) { 903 path = getValidatedPath(path); 904 return binding.unlink(pathModule.toNamespacedPath(path), kUsePromises); 905} 906 907async function fchmod(handle, mode) { 908 mode = parseFileMode(mode, 'mode'); 909 return binding.fchmod(handle.fd, mode, kUsePromises); 910} 911 912async function chmod(path, mode) { 913 path = getValidatedPath(path); 914 mode = parseFileMode(mode, 'mode'); 915 return binding.chmod(pathModule.toNamespacedPath(path), mode, kUsePromises); 916} 917 918async function lchmod(path, mode) { 919 if (O_SYMLINK === undefined) 920 throw new ERR_METHOD_NOT_IMPLEMENTED('lchmod()'); 921 922 const fd = await open(path, O_WRONLY | O_SYMLINK); 923 return handleFdClose(fchmod(fd, mode), fd.close); 924} 925 926async function lchown(path, uid, gid) { 927 path = getValidatedPath(path); 928 validateInteger(uid, 'uid', -1, kMaxUserId); 929 validateInteger(gid, 'gid', -1, kMaxUserId); 930 return binding.lchown(pathModule.toNamespacedPath(path), 931 uid, gid, kUsePromises); 932} 933 934async function fchown(handle, uid, gid) { 935 validateInteger(uid, 'uid', -1, kMaxUserId); 936 validateInteger(gid, 'gid', -1, kMaxUserId); 937 return binding.fchown(handle.fd, uid, gid, kUsePromises); 938} 939 940async function chown(path, uid, gid) { 941 path = getValidatedPath(path); 942 validateInteger(uid, 'uid', -1, kMaxUserId); 943 validateInteger(gid, 'gid', -1, kMaxUserId); 944 return binding.chown(pathModule.toNamespacedPath(path), 945 uid, gid, kUsePromises); 946} 947 948async function utimes(path, atime, mtime) { 949 path = getValidatedPath(path); 950 return binding.utimes(pathModule.toNamespacedPath(path), 951 toUnixTimestamp(atime), 952 toUnixTimestamp(mtime), 953 kUsePromises); 954} 955 956async function futimes(handle, atime, mtime) { 957 atime = toUnixTimestamp(atime, 'atime'); 958 mtime = toUnixTimestamp(mtime, 'mtime'); 959 return binding.futimes(handle.fd, atime, mtime, kUsePromises); 960} 961 962async function lutimes(path, atime, mtime) { 963 path = getValidatedPath(path); 964 return binding.lutimes(pathModule.toNamespacedPath(path), 965 toUnixTimestamp(atime), 966 toUnixTimestamp(mtime), 967 kUsePromises); 968} 969 970async function realpath(path, options) { 971 options = getOptions(options); 972 path = getValidatedPath(path); 973 return binding.realpath(path, options.encoding, kUsePromises); 974} 975 976async function mkdtemp(prefix, options) { 977 options = getOptions(options); 978 979 validateString(prefix, 'prefix'); 980 nullCheck(prefix); 981 warnOnNonPortableTemplate(prefix); 982 return binding.mkdtemp(`${prefix}XXXXXX`, options.encoding, kUsePromises); 983} 984 985async function writeFile(path, data, options) { 986 options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' }); 987 const flag = options.flag || 'w'; 988 989 if (!isArrayBufferView(data) && !isCustomIterable(data)) { 990 validatePrimitiveStringAfterArrayBufferView(data, 'data'); 991 data = Buffer.from(data, options.encoding || 'utf8'); 992 } 993 994 validateAbortSignal(options.signal); 995 if (path instanceof FileHandle) 996 return writeFileHandle(path, data, options.signal, options.encoding); 997 998 checkAborted(options.signal); 999 1000 const fd = await open(path, flag, options.mode); 1001 return handleFdClose( 1002 writeFileHandle(fd, data, options.signal, options.encoding), fd.close); 1003} 1004 1005function isCustomIterable(obj) { 1006 return isIterable(obj) && !isArrayBufferView(obj) && typeof obj !== 'string'; 1007} 1008 1009async function appendFile(path, data, options) { 1010 options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' }); 1011 options = copyObject(options); 1012 options.flag = options.flag || 'a'; 1013 return writeFile(path, data, options); 1014} 1015 1016async function readFile(path, options) { 1017 options = getOptions(options, { flag: 'r' }); 1018 const flag = options.flag || 'r'; 1019 1020 if (path instanceof FileHandle) 1021 return readFileHandle(path, options); 1022 1023 checkAborted(options.signal); 1024 1025 const fd = await open(path, flag, 0o666); 1026 return handleFdClose(readFileHandle(fd, options), fd.close); 1027} 1028 1029module.exports = { 1030 exports: { 1031 access, 1032 copyFile, 1033 cp, 1034 open, 1035 opendir: promisify(opendir), 1036 rename, 1037 truncate, 1038 rm, 1039 rmdir, 1040 mkdir, 1041 readdir, 1042 readlink, 1043 symlink, 1044 lstat, 1045 stat, 1046 statfs, 1047 link, 1048 unlink, 1049 chmod, 1050 lchmod, 1051 lchown, 1052 chown, 1053 utimes, 1054 lutimes, 1055 realpath, 1056 mkdtemp, 1057 writeFile, 1058 appendFile, 1059 readFile, 1060 watch, 1061 constants, 1062 }, 1063 1064 FileHandle, 1065 kRef, 1066 kUnref, 1067}; 1068