Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 3f0636d

Browse filesBrowse files
tetsuharuohzekiMoLow
authored andcommitted
fs: add support for mode flag to specify the copy behavior
`fs.copyFile()` supports copy-on-write operation if the underlying platform supports it by passing a mode flag. This behavior was added in a16d88d. This patch adds `mode` flag to `fs.cp()`, `fs.cpSync()`, and `fsPromises.cp()` to allow to change their behaviors to copy files. This test case is based on the test case that was introduced when we add `fs.constants.COPYFILE_FICLONE`. a16d88d. This test strategy is: - If the platform supports copy-on-write operation, check whether the destination is expected - Otherwise, the operation will fail and check whether the failure error information is expected. Fixes: #47080 PR-URL: #47084 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 6ab8927 commit 3f0636d
Copy full SHA for 3f0636d

File tree

Expand file treeCollapse file tree

5 files changed

+128
-3
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

5 files changed

+128
-3
lines changed
Open diff view settings
Collapse file

‎doc/api/fs.md‎

Copy file name to clipboardExpand all lines: doc/api/fs.md
+21-1Lines changed: 21 additions & 1 deletion
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,10 @@ try {
962962
<!-- YAML
963963
added: v16.7.0
964964
changes:
965+
- version: REPLACEME
966+
pr-url: https://github.com/nodejs/node/pull/47084
967+
description: Accept an additional `mode` option to specify
968+
the copy behavior as the `mode` argument of `fs.copyFile()`.
965969
- version: v17.6.0
966970
pr-url: https://github.com/nodejs/node/pull/41819
967971
description: Accepts an additional `verbatimSymlinks` option to specify
@@ -986,6 +990,8 @@ changes:
986990
operation will ignore errors if you set this to false and the destination
987991
exists. Use the `errorOnExist` option to change this behavior.
988992
**Default:** `true`.
993+
* `mode` {integer} modifiers for copy operation. **Default:** `0`.
994+
See `mode` flag of [`fsPromises.copyFile()`][].
989995
* `preserveTimestamps` {boolean} When `true` timestamps from `src` will
990996
be preserved. **Default:** `false`.
991997
* `recursive` {boolean} copy directories recursively **Default:** `false`
@@ -2269,6 +2275,10 @@ copyFile('source.txt', 'destination.txt', constants.COPYFILE_EXCL, callback);
22692275
<!-- YAML
22702276
added: v16.7.0
22712277
changes:
2278+
- version: REPLACEME
2279+
pr-url: https://github.com/nodejs/node/pull/47084
2280+
description: Accept an additional `mode` option to specify
2281+
the copy behavior as the `mode` argument of `fs.copyFile()`.
22722282
- version: v18.0.0
22732283
pr-url: https://github.com/nodejs/node/pull/41678
22742284
description: Passing an invalid callback to the `callback` argument
@@ -2298,6 +2308,8 @@ changes:
22982308
operation will ignore errors if you set this to false and the destination
22992309
exists. Use the `errorOnExist` option to change this behavior.
23002310
**Default:** `true`.
2311+
* `mode` {integer} modifiers for copy operation. **Default:** `0`.
2312+
See `mode` flag of [`fs.copyFile()`][].
23012313
* `preserveTimestamps` {boolean} When `true` timestamps from `src` will
23022314
be preserved. **Default:** `false`.
23032315
* `recursive` {boolean} copy directories recursively **Default:** `false`
@@ -5120,7 +5132,11 @@ copyFileSync('source.txt', 'destination.txt', constants.COPYFILE_EXCL);
51205132
<!-- YAML
51215133
added: v16.7.0
51225134
changes:
5123-
- version: v17.6.0
5135+
- version: REPLACEME
5136+
pr-url: https://github.com/nodejs/node/pull/47084
5137+
description: Accept an additional `mode` option to specify
5138+
the copy behavior as the `mode` argument of `fs.copyFile()`.
5139+
- version: v17.6.0
51245140
pr-url: https://github.com/nodejs/node/pull/41819
51255141
description: Accepts an additional `verbatimSymlinks` option to specify
51265142
whether to perform path resolution for symlinks.
@@ -5143,6 +5159,8 @@ changes:
51435159
operation will ignore errors if you set this to false and the destination
51445160
exists. Use the `errorOnExist` option to change this behavior.
51455161
**Default:** `true`.
5162+
* `mode` {integer} modifiers for copy operation. **Default:** `0`.
5163+
See `mode` flag of [`fs.copyFileSync()`][].
51465164
* `preserveTimestamps` {boolean} When `true` timestamps from `src` will
51475165
be preserved. **Default:** `false`.
51485166
* `recursive` {boolean} copy directories recursively **Default:** `false`
@@ -7907,6 +7925,7 @@ the file contents.
79077925
[`fs.chmod()`]: #fschmodpath-mode-callback
79087926
[`fs.chown()`]: #fschownpath-uid-gid-callback
79097927
[`fs.copyFile()`]: #fscopyfilesrc-dest-mode-callback
7928+
[`fs.copyFileSync()`]: #fscopyfilesyncsrc-dest-mode
79107929
[`fs.createReadStream()`]: #fscreatereadstreampath-options
79117930
[`fs.createWriteStream()`]: #fscreatewritestreampath-options
79127931
[`fs.exists()`]: #fsexistspath-callback
@@ -7940,6 +7959,7 @@ the file contents.
79407959
[`fs.writeFile()`]: #fswritefilefile-data-options-callback
79417960
[`fs.writev()`]: #fswritevfd-buffers-position-callback
79427961
[`fsPromises.access()`]: #fspromisesaccesspath-mode
7962+
[`fsPromises.copyFile()`]: #fspromisescopyfilesrc-dest-mode
79437963
[`fsPromises.open()`]: #fspromisesopenpath-flags-mode
79447964
[`fsPromises.opendir()`]: #fspromisesopendirpath-options
79457965
[`fsPromises.rm()`]: #fspromisesrmpath-options
Collapse file

‎lib/internal/fs/cp/cp-sync.js‎

Copy file name to clipboardExpand all lines: lib/internal/fs/cp/cp-sync.js
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ function mayCopyFile(srcStat, src, dest, opts) {
226226
}
227227

228228
function copyFile(srcStat, src, dest, opts) {
229-
copyFileSync(src, dest);
229+
copyFileSync(src, dest, opts.mode);
230230
if (opts.preserveTimestamps) handleTimestamps(srcStat.mode, src, dest);
231231
return setDestMode(dest, srcStat.mode);
232232
}
Collapse file

‎lib/internal/fs/cp/cp.js‎

Copy file name to clipboardExpand all lines: lib/internal/fs/cp/cp.js
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ async function mayCopyFile(srcStat, src, dest, opts) {
257257
}
258258

259259
async function _copyFile(srcStat, src, dest, opts) {
260-
await copyFile(src, dest);
260+
await copyFile(src, dest, opts.mode);
261261
if (opts.preserveTimestamps) {
262262
return handleTimestampsAndMode(srcStat.mode, src, dest);
263263
}
Collapse file

‎lib/internal/fs/utils.js‎

Copy file name to clipboardExpand all lines: lib/internal/fs/utils.js
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,7 @@ const validateCpOptions = hideStackFrames((options) => {
774774
validateBoolean(options.preserveTimestamps, 'options.preserveTimestamps');
775775
validateBoolean(options.recursive, 'options.recursive');
776776
validateBoolean(options.verbatimSymlinks, 'options.verbatimSymlinks');
777+
options.mode = getValidMode(options.mode, 'copyFile');
777778
if (options.dereference === true && options.verbatimSymlinks === true) {
778779
throw new ERR_INCOMPATIBLE_OPTION_PAIR('dereference', 'verbatimSymlinks');
779780
}
Collapse file

‎test/parallel/test-fs-cp.mjs‎

Copy file name to clipboardExpand all lines: test/parallel/test-fs-cp.mjs
+104Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,30 @@ function nextdir() {
3838
assertDirEquivalent(src, dest);
3939
}
4040

41+
// It copies a nested folder structure with mode flags.
42+
// This test is based on fs.promises.copyFile() with `COPYFILE_FICLONE_FORCE`.
43+
(() => {
44+
const src = './test/fixtures/copy/kitchen-sink';
45+
const dest = nextdir();
46+
try {
47+
cpSync(src, dest, mustNotMutateObjectDeep({
48+
recursive: true,
49+
mode: fs.constants.COPYFILE_FICLONE_FORCE,
50+
}));
51+
} catch (err) {
52+
// If the platform does not support `COPYFILE_FICLONE_FORCE` operation,
53+
// it should enter this path.
54+
assert.strictEqual(err.syscall, 'copyfile');
55+
assert(err.code === 'ENOTSUP' || err.code === 'ENOTTY' ||
56+
err.code === 'ENOSYS' || err.code === 'EXDEV');
57+
return;
58+
}
59+
60+
// If the platform support `COPYFILE_FICLONE_FORCE` operation,
61+
// it should reach to here.
62+
assertDirEquivalent(src, dest);
63+
})();
64+
4165
// It does not throw errors when directory is copied over and force is false.
4266
{
4367
const src = nextdir();
@@ -107,6 +131,14 @@ function nextdir() {
107131
});
108132
}
109133

134+
// It rejects if options.mode is invalid.
135+
{
136+
assert.throws(
137+
() => cpSync('a', 'b', { mode: -1 }),
138+
{ code: 'ERR_OUT_OF_RANGE' }
139+
);
140+
}
141+
110142

111143
// It throws an error when both dereference and verbatimSymlinks are enabled.
112144
{
@@ -425,6 +457,31 @@ if (!isWindows) {
425457
}));
426458
}
427459

460+
// It copies a nested folder structure with mode flags.
461+
// This test is based on fs.promises.copyFile() with `COPYFILE_FICLONE_FORCE`.
462+
{
463+
const src = './test/fixtures/copy/kitchen-sink';
464+
const dest = nextdir();
465+
cp(src, dest, mustNotMutateObjectDeep({
466+
recursive: true,
467+
mode: fs.constants.COPYFILE_FICLONE_FORCE,
468+
}), mustCall((err) => {
469+
if (!err) {
470+
// If the platform support `COPYFILE_FICLONE_FORCE` operation,
471+
// it should reach to here.
472+
assert.strictEqual(err, null);
473+
assertDirEquivalent(src, dest);
474+
return;
475+
}
476+
477+
// If the platform does not support `COPYFILE_FICLONE_FORCE` operation,
478+
// it should enter this path.
479+
assert.strictEqual(err.syscall, 'copyfile');
480+
assert(err.code === 'ENOTSUP' || err.code === 'ENOTTY' ||
481+
err.code === 'ENOSYS' || err.code === 'EXDEV');
482+
}));
483+
}
484+
428485
// It does not throw errors when directory is copied over and force is false.
429486
{
430487
const src = nextdir();
@@ -799,6 +856,14 @@ if (!isWindows) {
799856
);
800857
}
801858

859+
// It throws if options is not object.
860+
{
861+
assert.throws(
862+
() => cp('a', 'b', { mode: -1 }, () => {}),
863+
{ code: 'ERR_OUT_OF_RANGE' }
864+
);
865+
}
866+
802867
// Promises implementation of copy.
803868

804869
// It copies a nested folder structure with files and folders.
@@ -810,6 +875,35 @@ if (!isWindows) {
810875
assertDirEquivalent(src, dest);
811876
}
812877

878+
// It copies a nested folder structure with mode flags.
879+
// This test is based on fs.promises.copyFile() with `COPYFILE_FICLONE_FORCE`.
880+
{
881+
const src = './test/fixtures/copy/kitchen-sink';
882+
const dest = nextdir();
883+
let p = null;
884+
let successFiClone = false;
885+
try {
886+
p = await fs.promises.cp(src, dest, mustNotMutateObjectDeep({
887+
recursive: true,
888+
mode: fs.constants.COPYFILE_FICLONE_FORCE,
889+
}));
890+
successFiClone = true;
891+
} catch (err) {
892+
// If the platform does not support `COPYFILE_FICLONE_FORCE` operation,
893+
// it should enter this path.
894+
assert.strictEqual(err.syscall, 'copyfile');
895+
assert(err.code === 'ENOTSUP' || err.code === 'ENOTTY' ||
896+
err.code === 'ENOSYS' || err.code === 'EXDEV');
897+
}
898+
899+
if (successFiClone) {
900+
// If the platform support `COPYFILE_FICLONE_FORCE` operation,
901+
// it should reach to here.
902+
assert.strictEqual(p, undefined);
903+
assertDirEquivalent(src, dest);
904+
}
905+
}
906+
813907
// It accepts file URL as src and dest.
814908
{
815909
const src = './test/fixtures/copy/kitchen-sink';
@@ -847,6 +941,16 @@ if (!isWindows) {
847941
);
848942
}
849943

944+
// It rejects if options.mode is invalid.
945+
{
946+
await assert.rejects(
947+
fs.promises.cp('a', 'b', {
948+
mode: -1,
949+
}),
950+
{ code: 'ERR_OUT_OF_RANGE' }
951+
);
952+
}
953+
850954
function assertDirEquivalent(dir1, dir2) {
851955
const dir1Entries = [];
852956
collectEntries(dir1, dir1Entries);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.