From a16d88d9e9a6581e463082549823189aba25ca76 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Mon, 2 Apr 2018 15:12:57 -0400 Subject: [PATCH] fs: expose copy-on-write flags for fs.copyFile() This commit exposes the UV_FS_COPYFILE_FICLONE and UV_FS_COPYFILE_FICLONE_FORCE flags added in libuv 1.20.0. Fixes: https://github.com/nodejs/node/issues/19152 PR-URL: https://github.com/nodejs/node/pull/19759 Fixes: https://github.com/nodejs/node/issues/19152 Reviewed-By: Joyee Cheung Reviewed-By: Ben Noordhuis Reviewed-By: Santiago Gimeno Reviewed-By: James M Snell --- doc/api/fs.md | 70 ++++++++++++++++++++++++++++--- lib/fs.js | 10 ++++- src/node_constants.cc | 2 + test/parallel/test-fs-copyfile.js | 34 ++++++++++++++- 4 files changed, 107 insertions(+), 9 deletions(-) diff --git a/doc/api/fs.md b/doc/api/fs.md index d705a78f990647..c9adb5b16af001 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -1175,8 +1175,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: @@ -1216,8 +1226,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: @@ -3814,8 +3834,18 @@ 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: @@ -4449,6 +4479,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 85c939cc4a551e..780940b4f43b1b 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1916,7 +1916,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 2eaf622da7d4c4..8f119a27ba8218 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -1314,6 +1314,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 21e0838148b5ef..6e5d6c9403e795 100644 --- a/test/parallel/test-fs-copyfile.js +++ b/test/parallel/test-fs-copyfile.js @@ -8,7 +8,14 @@ const uv = process.binding('uv'); 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'); @@ -25,8 +32,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'); @@ -38,9 +51,26 @@ verify(src, dest); fs.copyFileSync(src, dest, 0); verify(src, dest); +// 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. -fs.unlinkSync(dest); +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);