summaryrefslogtreecommitdiff
path: root/lib/internal/fs/promises.js
diff options
context:
space:
mode:
authorcjihrig <cjihrig@gmail.com>2018-05-03 14:40:48 -0400
committercjihrig <cjihrig@gmail.com>2018-05-07 19:47:42 -0400
commit975f6c1f7077fd33db95eaeee6ccfa49e4a29348 (patch)
treeedbc5ece189e5b3747d5bb659203b55c8dafa580 /lib/internal/fs/promises.js
parent0452f1152cb15db01631196e893b875332946b76 (diff)
downloadandroid-node-v8-975f6c1f7077fd33db95eaeee6ccfa49e4a29348.tar.gz
android-node-v8-975f6c1f7077fd33db95eaeee6ccfa49e4a29348.tar.bz2
android-node-v8-975f6c1f7077fd33db95eaeee6ccfa49e4a29348.zip
fs: move fs/promises to fs.promises
PR-URL: https://github.com/nodejs/node/pull/20504 Refs: https://github.com/nodejs/TSC/issues/389 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Myles Borins <myles.borins@gmail.com> Reviewed-By: Shingo Inoue <leko.noor@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com>
Diffstat (limited to 'lib/internal/fs/promises.js')
-rw-r--r--lib/internal/fs/promises.js517
1 files changed, 517 insertions, 0 deletions
diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js
new file mode 100644
index 0000000000..6b523473e4
--- /dev/null
+++ b/lib/internal/fs/promises.js
@@ -0,0 +1,517 @@
+'use strict';
+
+const {
+ F_OK,
+ O_SYMLINK,
+ O_WRONLY,
+ S_IFMT,
+ S_IFREG
+} = process.binding('constants').fs;
+const binding = process.binding('fs');
+const { Buffer, kMaxLength } = require('buffer');
+const {
+ ERR_FS_FILE_TOO_LARGE,
+ ERR_INVALID_ARG_TYPE,
+ ERR_METHOD_NOT_IMPLEMENTED,
+ ERR_OUT_OF_RANGE
+} = require('internal/errors').codes;
+const { getPathFromURL } = require('internal/url');
+const { isUint8Array } = require('internal/util/types');
+const {
+ copyObject,
+ getOptions,
+ getStatsFromBinding,
+ modeNum,
+ nullCheck,
+ preprocessSymlinkDestination,
+ stringToFlags,
+ stringToSymlinkType,
+ toUnixTimestamp,
+ validateBuffer,
+ validateOffsetLengthRead,
+ validateOffsetLengthWrite,
+ validatePath
+} = require('internal/fs/utils');
+const {
+ isUint32,
+ validateInt32,
+ validateUint32
+} = require('internal/validators');
+const pathModule = require('path');
+
+const kHandle = Symbol('handle');
+const { kUsePromises } = binding;
+
+class FileHandle {
+ constructor(filehandle) {
+ this[kHandle] = filehandle;
+ }
+
+ getAsyncId() {
+ return this[kHandle].getAsyncId();
+ }
+
+ get fd() {
+ return this[kHandle].fd;
+ }
+
+ appendFile(data, options) {
+ return appendFile(this, data, options);
+ }
+
+ chmod(mode) {
+ return fchmod(this, mode);
+ }
+
+ chown(uid, gid) {
+ return fchown(this, uid, gid);
+ }
+
+ datasync() {
+ return fdatasync(this);
+ }
+
+ sync() {
+ return fsync(this);
+ }
+
+ read(buffer, offset, length, position) {
+ return read(this, buffer, offset, length, position);
+ }
+
+ readFile(options) {
+ return readFile(this, options);
+ }
+
+ stat() {
+ return fstat(this);
+ }
+
+ truncate(len = 0) {
+ return ftruncate(this, len);
+ }
+
+ utimes(atime, mtime) {
+ return futimes(this, atime, mtime);
+ }
+
+ write(buffer, offset, length, position) {
+ return write(this, buffer, offset, length, position);
+ }
+
+ writeFile(data, options) {
+ return writeFile(this, data, options);
+ }
+
+ close() {
+ return this[kHandle].close();
+ }
+}
+
+
+function validateFileHandle(handle) {
+ if (!(handle instanceof FileHandle))
+ throw new ERR_INVALID_ARG_TYPE('filehandle', 'FileHandle', handle);
+}
+
+async function writeFileHandle(filehandle, data, options) {
+ let buffer = isUint8Array(data) ?
+ data : Buffer.from('' + data, options.encoding || 'utf8');
+ let remaining = buffer.length;
+ if (remaining === 0) return;
+ do {
+ const { bytesWritten } =
+ await write(filehandle, buffer, 0,
+ Math.min(16384, buffer.length));
+ remaining -= bytesWritten;
+ buffer = buffer.slice(bytesWritten);
+ } while (remaining > 0);
+}
+
+async function readFileHandle(filehandle, options) {
+ const statFields = await binding.fstat(filehandle.fd, kUsePromises);
+
+ let size;
+ if ((statFields[1/* mode */] & S_IFMT) === S_IFREG) {
+ size = statFields[8/* size */];
+ } else {
+ size = 0;
+ }
+
+ if (size === 0)
+ return Buffer.alloc(0);
+
+ if (size > kMaxLength)
+ throw new ERR_FS_FILE_TOO_LARGE(size);
+
+ const chunks = [];
+ const chunkSize = Math.min(size, 16384);
+ let totalRead = 0;
+ let endOfFile = false;
+ do {
+ const buf = Buffer.alloc(chunkSize);
+ const { bytesRead, buffer } =
+ await read(filehandle, buf, 0, chunkSize, totalRead);
+ totalRead += bytesRead;
+ endOfFile = bytesRead !== chunkSize;
+ if (bytesRead > 0)
+ chunks.push(buffer.slice(0, bytesRead));
+ } while (!endOfFile);
+
+ const result = Buffer.concat(chunks);
+ if (options.encoding) {
+ return result.toString(options.encoding);
+ } else {
+ return result;
+ }
+}
+
+// All of the functions are defined as async in order to ensure that errors
+// thrown cause promise rejections rather than being thrown synchronously.
+async function access(path, mode = F_OK) {
+ path = getPathFromURL(path);
+ validatePath(path);
+
+ mode = mode | 0;
+ return binding.access(pathModule.toNamespacedPath(path), mode,
+ kUsePromises);
+}
+
+async function copyFile(src, dest, flags) {
+ src = getPathFromURL(src);
+ dest = getPathFromURL(dest);
+ validatePath(src, 'src');
+ validatePath(dest, 'dest');
+ flags = flags | 0;
+ return binding.copyFile(pathModule.toNamespacedPath(src),
+ pathModule.toNamespacedPath(dest),
+ flags, kUsePromises);
+}
+
+// Note that unlike fs.open() which uses numeric file descriptors,
+// fsPromises.open() uses the fs.FileHandle class.
+async function open(path, flags, mode) {
+ mode = modeNum(mode, 0o666);
+ path = getPathFromURL(path);
+ validatePath(path);
+ validateUint32(mode, 'mode');
+ return new FileHandle(
+ await binding.openFileHandle(pathModule.toNamespacedPath(path),
+ stringToFlags(flags),
+ mode, kUsePromises));
+}
+
+async function read(handle, buffer, offset, length, position) {
+ validateFileHandle(handle);
+ validateBuffer(buffer);
+
+ offset |= 0;
+ length |= 0;
+
+ if (length === 0)
+ return { bytesRead: length, buffer };
+
+ validateOffsetLengthRead(offset, length, buffer.length);
+
+ if (!isUint32(position))
+ position = -1;
+
+ const bytesRead = (await binding.read(handle.fd, buffer, offset, length,
+ position, kUsePromises)) || 0;
+
+ return { bytesRead, buffer };
+}
+
+async function write(handle, buffer, offset, length, position) {
+ validateFileHandle(handle);
+
+ if (buffer.length === 0)
+ return { bytesWritten: 0, buffer };
+
+ if (isUint8Array(buffer)) {
+ if (typeof offset !== 'number')
+ offset = 0;
+ if (typeof length !== 'number')
+ length = buffer.length - offset;
+ if (typeof position !== 'number')
+ position = null;
+ validateOffsetLengthWrite(offset, length, buffer.byteLength);
+ const bytesWritten =
+ (await binding.writeBuffer(handle.fd, buffer, offset,
+ length, position, kUsePromises)) || 0;
+ return { bytesWritten, buffer };
+ }
+
+ if (typeof buffer !== 'string')
+ buffer += '';
+ if (typeof position !== 'function') {
+ if (typeof offset === 'function') {
+ position = offset;
+ offset = null;
+ } else {
+ position = length;
+ }
+ length = 'utf8';
+ }
+ const bytesWritten = (await binding.writeString(handle.fd, buffer, offset,
+ length, kUsePromises)) || 0;
+ return { bytesWritten, buffer };
+}
+
+async function rename(oldPath, newPath) {
+ oldPath = getPathFromURL(oldPath);
+ newPath = getPathFromURL(newPath);
+ validatePath(oldPath, 'oldPath');
+ validatePath(newPath, 'newPath');
+ return binding.rename(pathModule.toNamespacedPath(oldPath),
+ pathModule.toNamespacedPath(newPath),
+ kUsePromises);
+}
+
+async function truncate(path, len = 0) {
+ return ftruncate(await open(path, 'r+'), len);
+}
+
+async function ftruncate(handle, len = 0) {
+ validateFileHandle(handle);
+ validateInt32(len, 'len');
+ len = Math.max(0, len);
+ return binding.ftruncate(handle.fd, len, kUsePromises);
+}
+
+async function rmdir(path) {
+ path = getPathFromURL(path);
+ validatePath(path);
+ return binding.rmdir(pathModule.toNamespacedPath(path), kUsePromises);
+}
+
+async function fdatasync(handle) {
+ validateFileHandle(handle);
+ return binding.fdatasync(handle.fd, kUsePromises);
+}
+
+async function fsync(handle) {
+ validateFileHandle(handle);
+ return binding.fsync(handle.fd, kUsePromises);
+}
+
+async function mkdir(path, mode) {
+ mode = modeNum(mode, 0o777);
+ path = getPathFromURL(path);
+ validatePath(path);
+ validateUint32(mode, 'mode');
+ return binding.mkdir(pathModule.toNamespacedPath(path), mode, kUsePromises);
+}
+
+async function readdir(path, options) {
+ options = getOptions(options, {});
+ path = getPathFromURL(path);
+ validatePath(path);
+ return binding.readdir(pathModule.toNamespacedPath(path),
+ options.encoding, kUsePromises);
+}
+
+async function readlink(path, options) {
+ options = getOptions(options, {});
+ path = getPathFromURL(path);
+ validatePath(path, 'oldPath');
+ return binding.readlink(pathModule.toNamespacedPath(path),
+ options.encoding, kUsePromises);
+}
+
+async function symlink(target, path, type_) {
+ const type = (typeof type_ === 'string' ? type_ : null);
+ target = getPathFromURL(target);
+ path = getPathFromURL(path);
+ validatePath(target, 'target');
+ validatePath(path);
+ return binding.symlink(preprocessSymlinkDestination(target, type, path),
+ pathModule.toNamespacedPath(path),
+ stringToSymlinkType(type),
+ kUsePromises);
+}
+
+async function fstat(handle) {
+ validateFileHandle(handle);
+ const result = await binding.fstat(handle.fd, kUsePromises);
+ return getStatsFromBinding(result);
+}
+
+async function lstat(path) {
+ path = getPathFromURL(path);
+ validatePath(path);
+ const result = await binding.lstat(pathModule.toNamespacedPath(path),
+ kUsePromises);
+ return getStatsFromBinding(result);
+}
+
+async function stat(path) {
+ path = getPathFromURL(path);
+ validatePath(path);
+ const result = await binding.stat(pathModule.toNamespacedPath(path),
+ kUsePromises);
+ return getStatsFromBinding(result);
+}
+
+async function link(existingPath, newPath) {
+ existingPath = getPathFromURL(existingPath);
+ newPath = getPathFromURL(newPath);
+ validatePath(existingPath, 'existingPath');
+ validatePath(newPath, 'newPath');
+ return binding.link(pathModule.toNamespacedPath(existingPath),
+ pathModule.toNamespacedPath(newPath),
+ kUsePromises);
+}
+
+async function unlink(path) {
+ path = getPathFromURL(path);
+ validatePath(path);
+ return binding.unlink(pathModule.toNamespacedPath(path), kUsePromises);
+}
+
+async function fchmod(handle, mode) {
+ mode = modeNum(mode);
+ validateFileHandle(handle);
+ validateUint32(mode, 'mode');
+ if (mode > 0o777)
+ throw new ERR_OUT_OF_RANGE('mode', undefined, mode);
+ return binding.fchmod(handle.fd, mode, kUsePromises);
+}
+
+async function chmod(path, mode) {
+ path = getPathFromURL(path);
+ validatePath(path);
+ mode = modeNum(mode);
+ validateUint32(mode, 'mode');
+ return binding.chmod(pathModule.toNamespacedPath(path), mode, kUsePromises);
+}
+
+async function lchmod(path, mode) {
+ if (O_SYMLINK !== undefined) {
+ const fd = await open(path,
+ O_WRONLY | O_SYMLINK);
+ return fchmod(fd, mode).finally(fd.close.bind(fd));
+ }
+ throw new ERR_METHOD_NOT_IMPLEMENTED();
+}
+
+async function lchown(path, uid, gid) {
+ if (O_SYMLINK !== undefined) {
+ const fd = await open(path,
+ O_WRONLY | O_SYMLINK);
+ return fchmod(fd, uid, gid).finally(fd.close.bind(fd));
+ }
+ throw new ERR_METHOD_NOT_IMPLEMENTED();
+}
+
+async function fchown(handle, uid, gid) {
+ validateFileHandle(handle);
+ validateUint32(uid, 'uid');
+ validateUint32(gid, 'gid');
+ return binding.fchown(handle.fd, uid, gid, kUsePromises);
+}
+
+async function chown(path, uid, gid) {
+ path = getPathFromURL(path);
+ validatePath(path);
+ validateUint32(uid, 'uid');
+ validateUint32(gid, 'gid');
+ return binding.chown(pathModule.toNamespacedPath(path),
+ uid, gid, kUsePromises);
+}
+
+async function utimes(path, atime, mtime) {
+ path = getPathFromURL(path);
+ validatePath(path);
+ return binding.utimes(pathModule.toNamespacedPath(path),
+ toUnixTimestamp(atime),
+ toUnixTimestamp(mtime),
+ kUsePromises);
+}
+
+async function futimes(handle, atime, mtime) {
+ validateFileHandle(handle);
+ atime = toUnixTimestamp(atime, 'atime');
+ mtime = toUnixTimestamp(mtime, 'mtime');
+ return binding.futimes(handle.fd, atime, mtime, kUsePromises);
+}
+
+async function realpath(path, options) {
+ options = getOptions(options, {});
+ path = getPathFromURL(path);
+ validatePath(path);
+ return binding.realpath(path, options.encoding, kUsePromises);
+}
+
+async function mkdtemp(prefix, options) {
+ options = getOptions(options, {});
+ if (!prefix || typeof prefix !== 'string') {
+ throw new ERR_INVALID_ARG_TYPE('prefix', 'string', prefix);
+ }
+ nullCheck(prefix);
+ return binding.mkdtemp(`${prefix}XXXXXX`, options.encoding, kUsePromises);
+}
+
+async function writeFile(path, data, options) {
+ options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
+ const flag = options.flag || 'w';
+
+ if (path instanceof FileHandle)
+ return writeFileHandle(path, data, options);
+
+ const fd = await open(path, flag, options.mode);
+ return writeFileHandle(fd, data, options).finally(fd.close.bind(fd));
+}
+
+async function appendFile(path, data, options) {
+ options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'a' });
+ options = copyObject(options);
+ options.flag = options.flag || 'a';
+ return writeFile(path, data, options);
+}
+
+async function readFile(path, options) {
+ options = getOptions(options, { flag: 'r' });
+
+ if (path instanceof FileHandle)
+ return readFileHandle(path, options);
+
+ const fd = await open(path, options.flag, 0o666);
+ return readFileHandle(fd, options).finally(fd.close.bind(fd));
+}
+
+module.exports = {
+ access,
+ copyFile,
+ open,
+ read,
+ write,
+ rename,
+ truncate,
+ ftruncate,
+ rmdir,
+ fdatasync,
+ fsync,
+ mkdir,
+ readdir,
+ readlink,
+ symlink,
+ fstat,
+ lstat,
+ stat,
+ link,
+ unlink,
+ fchmod,
+ chmod,
+ lchmod,
+ lchown,
+ fchown,
+ chown,
+ utimes,
+ futimes,
+ realpath,
+ mkdtemp,
+ writeFile,
+ appendFile,
+ readFile
+};