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