diff --git a/doc/api/fs.md b/doc/api/fs.md index 8f2a75d54964a4..837ef0256a49e9 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -1159,8 +1159,18 @@ operation. If an error occurs after the destination file has been opened for writing, Node.js will attempt to remove the destination. `flags` is an optional integer that specifies the behavior -of the copy operation. The only supported flag is `fs.constants.COPYFILE_EXCL`, -which causes the copy operation to fail if `dest` already exists. +of the copy operation. It is possible to create a mask consisting of the bitwise +OR of two or more values (e.g. +`fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE`). + +* `fs.constants.COPYFILE_EXCL` - The copy operation will fail if `dest` already +exists. +* `fs.constants.COPYFILE_FICLONE` - The copy operation will attempt to create a +copy-on-write reflink. If the platform does not support copy-on-write, then a +fallback copy mechanism is used. +* `fs.constants.COPYFILE_FICLONE_FORCE` - The copy operation will attempt to +create a copy-on-write reflink. If the platform does not support copy-on-write, +then the operation will fail. Example: @@ -1200,8 +1210,18 @@ atomicity of the copy operation. If an error occurs after the destination file has been opened for writing, Node.js will attempt to remove the destination. `flags` is an optional integer that specifies the behavior -of the copy operation. The only supported flag is `fs.constants.COPYFILE_EXCL`, -which causes the copy operation to fail if `dest` already exists. +of the copy operation. It is possible to create a mask consisting of the bitwise +OR of two or more values (e.g. +`fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE`). + +* `fs.constants.COPYFILE_EXCL` - The copy operation will fail if `dest` already +exists. +* `fs.constants.COPYFILE_FICLONE` - The copy operation will attempt to create a +copy-on-write reflink. If the platform does not support copy-on-write, then a +fallback copy mechanism is used. +* `fs.constants.COPYFILE_FICLONE_FORCE` - The copy operation will attempt to +create a copy-on-write reflink. If the platform does not support copy-on-write, +then the operation will fail. Example: @@ -3336,6 +3356,34 @@ The following constants are meant for use with [`fs.access()`][]. +### File Copy Constants + +The following constants are meant for use with [`fs.copyFile()`][]. + + + + + + + + + + + + + + + + + + +
ConstantDescription
COPYFILE_EXCLIf present, the copy operation will fail with an error if the + destination path already exists.
COPYFILE_FICLONEIf present, the copy operation will attempt to create a + copy-on-write reflink. If the underlying platform does not support + copy-on-write, then a fallback copy mechanism is used.
COPYFILE_FICLONE_FORCEIf present, the copy operation will attempt to create a + copy-on-write reflink. If the underlying platform does not support + copy-on-write, then the operation will fail with an error.
+ ### File Open Constants The following constants are meant for use with `fs.open()`. diff --git a/lib/fs.js b/lib/fs.js index 5d3bae1550e298..f89c501e41f188 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1920,7 +1920,15 @@ fs.mkdtempSync = function(prefix, options) { // Define copyFile() flags. Object.defineProperties(fs.constants, { - COPYFILE_EXCL: { enumerable: true, value: constants.UV_FS_COPYFILE_EXCL } + COPYFILE_EXCL: { enumerable: true, value: constants.UV_FS_COPYFILE_EXCL }, + COPYFILE_FICLONE: { + enumerable: true, + value: constants.UV_FS_COPYFILE_FICLONE + }, + COPYFILE_FICLONE_FORCE: { + enumerable: true, + value: constants.UV_FS_COPYFILE_FICLONE_FORCE + } }); diff --git a/src/node_constants.cc b/src/node_constants.cc index d478d434000fca..be1965a12c0c38 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -1317,6 +1317,8 @@ void DefineConstants(v8::Isolate* isolate, Local target) { // Define libuv constants. NODE_DEFINE_CONSTANT(os_constants, UV_UDP_REUSEADDR); NODE_DEFINE_CONSTANT(fs_constants, UV_FS_COPYFILE_EXCL); + NODE_DEFINE_CONSTANT(fs_constants, UV_FS_COPYFILE_FICLONE); + NODE_DEFINE_CONSTANT(fs_constants, UV_FS_COPYFILE_FICLONE_FORCE); os_constants->Set(OneByteString(isolate, "dlopen"), dlopen_constants); os_constants->Set(OneByteString(isolate, "errno"), err_constants); diff --git a/test/parallel/test-fs-copyfile.js b/test/parallel/test-fs-copyfile.js index 574ea61c9f52a2..1d6a072a9773b1 100644 --- a/test/parallel/test-fs-copyfile.js +++ b/test/parallel/test-fs-copyfile.js @@ -7,7 +7,14 @@ const fs = require('fs'); const path = require('path'); const src = fixtures.path('a.js'); const dest = path.join(tmpdir.path, 'copyfile.out'); -const { COPYFILE_EXCL, UV_FS_COPYFILE_EXCL } = fs.constants; +const { + COPYFILE_EXCL, + COPYFILE_FICLONE, + COPYFILE_FICLONE_FORCE, + UV_FS_COPYFILE_EXCL, + UV_FS_COPYFILE_FICLONE, + UV_FS_COPYFILE_FICLONE_FORCE +} = fs.constants; function verify(src, dest) { const srcData = fs.readFileSync(src, 'utf8'); @@ -24,8 +31,14 @@ tmpdir.refresh(); // Verify that flags are defined. assert.strictEqual(typeof COPYFILE_EXCL, 'number'); +assert.strictEqual(typeof COPYFILE_FICLONE, 'number'); +assert.strictEqual(typeof COPYFILE_FICLONE_FORCE, 'number'); assert.strictEqual(typeof UV_FS_COPYFILE_EXCL, 'number'); +assert.strictEqual(typeof UV_FS_COPYFILE_FICLONE, 'number'); +assert.strictEqual(typeof UV_FS_COPYFILE_FICLONE_FORCE, 'number'); assert.strictEqual(COPYFILE_EXCL, UV_FS_COPYFILE_EXCL); +assert.strictEqual(COPYFILE_FICLONE, UV_FS_COPYFILE_FICLONE); +assert.strictEqual(COPYFILE_FICLONE_FORCE, UV_FS_COPYFILE_FICLONE_FORCE); // Verify that files are overwritten when no flags are provided. fs.writeFileSync(dest, '', 'utf8'); @@ -47,8 +60,26 @@ assert.throws(() => { fs.copyFileSync(`${src}__does_not_exist`, dest, COPYFILE_EXCL); }, /^Error: ENOENT: no such file or directory, copyfile/); -// Copies asynchronously. +// Verify that UV_FS_COPYFILE_FICLONE can be used. fs.unlinkSync(dest); +fs.copyFileSync(src, dest, UV_FS_COPYFILE_FICLONE); +verify(src, dest); + +// Verify that COPYFILE_FICLONE_FORCE can be used. +try { + fs.unlinkSync(dest); + fs.copyFileSync(src, dest, COPYFILE_FICLONE_FORCE); + verify(src, dest); +} catch (err) { + assert.strictEqual(err.syscall, 'copyfile'); + assert(err.code === 'ENOTSUP' || err.code === 'ENOTTY' || + err.code === 'ENOSYS'); + assert.strictEqual(err.path, src); + assert.strictEqual(err.dest, dest); +} + +// Copies asynchronously. +tmpdir.refresh(); // Don't use unlinkSync() since the last test may fail. fs.copyFile(src, dest, common.mustCall((err) => { assert.ifError(err); verify(src, dest);