Skip to content

Commit a16d88d

Browse files
committed
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: #19152 PR-URL: #19759 Fixes: #19152 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 6179467 commit a16d88d

File tree

4 files changed

+107
-9
lines changed

4 files changed

+107
-9
lines changed

doc/api/fs.md

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,8 +1175,18 @@ operation. If an error occurs after the destination file has been opened for
11751175
writing, Node.js will attempt to remove the destination.
11761176

11771177
`flags` is an optional integer that specifies the behavior
1178-
of the copy operation. The only supported flag is `fs.constants.COPYFILE_EXCL`,
1179-
which causes the copy operation to fail if `dest` already exists.
1178+
of the copy operation. It is possible to create a mask consisting of the bitwise
1179+
OR of two or more values (e.g.
1180+
`fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE`).
1181+
1182+
* `fs.constants.COPYFILE_EXCL` - The copy operation will fail if `dest` already
1183+
exists.
1184+
* `fs.constants.COPYFILE_FICLONE` - The copy operation will attempt to create a
1185+
copy-on-write reflink. If the platform does not support copy-on-write, then a
1186+
fallback copy mechanism is used.
1187+
* `fs.constants.COPYFILE_FICLONE_FORCE` - The copy operation will attempt to
1188+
create a copy-on-write reflink. If the platform does not support copy-on-write,
1189+
then the operation will fail.
11801190

11811191
Example:
11821192

@@ -1216,8 +1226,18 @@ atomicity of the copy operation. If an error occurs after the destination file
12161226
has been opened for writing, Node.js will attempt to remove the destination.
12171227

12181228
`flags` is an optional integer that specifies the behavior
1219-
of the copy operation. The only supported flag is `fs.constants.COPYFILE_EXCL`,
1220-
which causes the copy operation to fail if `dest` already exists.
1229+
of the copy operation. It is possible to create a mask consisting of the bitwise
1230+
OR of two or more values (e.g.
1231+
`fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE`).
1232+
1233+
* `fs.constants.COPYFILE_EXCL` - The copy operation will fail if `dest` already
1234+
exists.
1235+
* `fs.constants.COPYFILE_FICLONE` - The copy operation will attempt to create a
1236+
copy-on-write reflink. If the platform does not support copy-on-write, then a
1237+
fallback copy mechanism is used.
1238+
* `fs.constants.COPYFILE_FICLONE_FORCE` - The copy operation will attempt to
1239+
create a copy-on-write reflink. If the platform does not support copy-on-write,
1240+
then the operation will fail.
12211241

12221242
Example:
12231243

@@ -3814,8 +3834,18 @@ error occurs after the destination file has been opened for writing, Node.js
38143834
will attempt to remove the destination.
38153835

38163836
`flags` is an optional integer that specifies the behavior
3817-
of the copy operation. The only supported flag is `fs.constants.COPYFILE_EXCL`,
3818-
which causes the copy operation to fail if `dest` already exists.
3837+
of the copy operation. It is possible to create a mask consisting of the bitwise
3838+
OR of two or more values (e.g.
3839+
`fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE`).
3840+
3841+
* `fs.constants.COPYFILE_EXCL` - The copy operation will fail if `dest` already
3842+
exists.
3843+
* `fs.constants.COPYFILE_FICLONE` - The copy operation will attempt to create a
3844+
copy-on-write reflink. If the platform does not support copy-on-write, then a
3845+
fallback copy mechanism is used.
3846+
* `fs.constants.COPYFILE_FICLONE_FORCE` - The copy operation will attempt to
3847+
create a copy-on-write reflink. If the platform does not support copy-on-write,
3848+
then the operation will fail.
38193849

38203850
Example:
38213851

@@ -4449,6 +4479,34 @@ The following constants are meant for use with [`fs.access()`][].
44494479
</tr>
44504480
</table>
44514481

4482+
### File Copy Constants
4483+
4484+
The following constants are meant for use with [`fs.copyFile()`][].
4485+
4486+
<table>
4487+
<tr>
4488+
<th>Constant</th>
4489+
<th>Description</th>
4490+
</tr>
4491+
<tr>
4492+
<td><code>COPYFILE_EXCL</code></td>
4493+
<td>If present, the copy operation will fail with an error if the
4494+
destination path already exists.</td>
4495+
</tr>
4496+
<tr>
4497+
<td><code>COPYFILE_FICLONE</code></td>
4498+
<td>If present, the copy operation will attempt to create a
4499+
copy-on-write reflink. If the underlying platform does not support
4500+
copy-on-write, then a fallback copy mechanism is used.</td>
4501+
</tr>
4502+
<tr>
4503+
<td><code>COPYFILE_FICLONE_FORCE</code></td>
4504+
<td>If present, the copy operation will attempt to create a
4505+
copy-on-write reflink. If the underlying platform does not support
4506+
copy-on-write, then the operation will fail with an error.</td>
4507+
</tr>
4508+
</table>
4509+
44524510
### File Open Constants
44534511

44544512
The following constants are meant for use with `fs.open()`.

lib/fs.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1916,7 +1916,15 @@ fs.mkdtempSync = function(prefix, options) {
19161916

19171917
// Define copyFile() flags.
19181918
Object.defineProperties(fs.constants, {
1919-
COPYFILE_EXCL: { enumerable: true, value: constants.UV_FS_COPYFILE_EXCL }
1919+
COPYFILE_EXCL: { enumerable: true, value: constants.UV_FS_COPYFILE_EXCL },
1920+
COPYFILE_FICLONE: {
1921+
enumerable: true,
1922+
value: constants.UV_FS_COPYFILE_FICLONE
1923+
},
1924+
COPYFILE_FICLONE_FORCE: {
1925+
enumerable: true,
1926+
value: constants.UV_FS_COPYFILE_FICLONE_FORCE
1927+
}
19201928
});
19211929

19221930

src/node_constants.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,8 @@ void DefineConstants(v8::Isolate* isolate, Local<Object> target) {
13141314
// Define libuv constants.
13151315
NODE_DEFINE_CONSTANT(os_constants, UV_UDP_REUSEADDR);
13161316
NODE_DEFINE_CONSTANT(fs_constants, UV_FS_COPYFILE_EXCL);
1317+
NODE_DEFINE_CONSTANT(fs_constants, UV_FS_COPYFILE_FICLONE);
1318+
NODE_DEFINE_CONSTANT(fs_constants, UV_FS_COPYFILE_FICLONE_FORCE);
13171319

13181320
os_constants->Set(OneByteString(isolate, "dlopen"), dlopen_constants);
13191321
os_constants->Set(OneByteString(isolate, "errno"), err_constants);

test/parallel/test-fs-copyfile.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,14 @@ const uv = process.binding('uv');
88
const path = require('path');
99
const src = fixtures.path('a.js');
1010
const dest = path.join(tmpdir.path, 'copyfile.out');
11-
const { COPYFILE_EXCL, UV_FS_COPYFILE_EXCL } = fs.constants;
11+
const {
12+
COPYFILE_EXCL,
13+
COPYFILE_FICLONE,
14+
COPYFILE_FICLONE_FORCE,
15+
UV_FS_COPYFILE_EXCL,
16+
UV_FS_COPYFILE_FICLONE,
17+
UV_FS_COPYFILE_FICLONE_FORCE
18+
} = fs.constants;
1219

1320
function verify(src, dest) {
1421
const srcData = fs.readFileSync(src, 'utf8');
@@ -25,8 +32,14 @@ tmpdir.refresh();
2532

2633
// Verify that flags are defined.
2734
assert.strictEqual(typeof COPYFILE_EXCL, 'number');
35+
assert.strictEqual(typeof COPYFILE_FICLONE, 'number');
36+
assert.strictEqual(typeof COPYFILE_FICLONE_FORCE, 'number');
2837
assert.strictEqual(typeof UV_FS_COPYFILE_EXCL, 'number');
38+
assert.strictEqual(typeof UV_FS_COPYFILE_FICLONE, 'number');
39+
assert.strictEqual(typeof UV_FS_COPYFILE_FICLONE_FORCE, 'number');
2940
assert.strictEqual(COPYFILE_EXCL, UV_FS_COPYFILE_EXCL);
41+
assert.strictEqual(COPYFILE_FICLONE, UV_FS_COPYFILE_FICLONE);
42+
assert.strictEqual(COPYFILE_FICLONE_FORCE, UV_FS_COPYFILE_FICLONE_FORCE);
3043

3144
// Verify that files are overwritten when no flags are provided.
3245
fs.writeFileSync(dest, '', 'utf8');
@@ -38,9 +51,26 @@ verify(src, dest);
3851
fs.copyFileSync(src, dest, 0);
3952
verify(src, dest);
4053

54+
// Verify that UV_FS_COPYFILE_FICLONE can be used.
55+
fs.unlinkSync(dest);
56+
fs.copyFileSync(src, dest, UV_FS_COPYFILE_FICLONE);
57+
verify(src, dest);
58+
59+
// Verify that COPYFILE_FICLONE_FORCE can be used.
60+
try {
61+
fs.unlinkSync(dest);
62+
fs.copyFileSync(src, dest, COPYFILE_FICLONE_FORCE);
63+
verify(src, dest);
64+
} catch (err) {
65+
assert.strictEqual(err.syscall, 'copyfile');
66+
assert(err.code === 'ENOTSUP' || err.code === 'ENOTTY' ||
67+
err.code === 'ENOSYS');
68+
assert.strictEqual(err.path, src);
69+
assert.strictEqual(err.dest, dest);
70+
}
4171

4272
// Copies asynchronously.
43-
fs.unlinkSync(dest);
73+
tmpdir.refresh(); // Don't use unlinkSync() since the last test may fail.
4474
fs.copyFile(src, dest, common.mustCall((err) => {
4575
assert.ifError(err);
4676
verify(src, dest);

0 commit comments

Comments
 (0)