diff --git a/doc/api/fs.md b/doc/api/fs.md index 316b1b30111e06..4a04a18119934c 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -593,7 +593,7 @@ try { ``` If the file previously was shorter than `len` bytes, it is extended, and the -extended part is filled with null bytes (`'\0'`): +extended part is filled with null bytes (`'\0'`). If `len` is negative then `0` will be used. @@ -1481,6 +1481,10 @@ automatically be normalized to absolute path. * `path` {string|Buffer|URL} @@ -4073,6 +4077,9 @@ $ tree . * `path` {string|Buffer|URL} diff --git a/lib/fs.js b/lib/fs.js index ffa216f35388e0..ee49dac4ed169c 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1060,7 +1060,7 @@ function truncate(path, len, callback) { validateInteger(len, 'len'); len = MathMax(0, len); callback = maybeCallback(callback); - fs.open(path, 'r+', (er, fd) => { + fs.open(path, 'a', (er, fd) => { if (er) return callback(er); const req = new FSReqCallback(); req.oncomplete = function oncomplete(er) { @@ -1088,7 +1088,7 @@ function truncateSync(path, len) { len = 0; } // Allow error to be thrown, but still close fd. - const fd = fs.openSync(path, 'r+'); + const fd = fs.openSync(path, 'a'); let ret; try { diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index 34fd0f586766dd..0f866556ba153e 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -635,7 +635,7 @@ async function rename(oldPath, newPath) { } async function truncate(path, len = 0) { - const fd = await open(path, 'r+'); + const fd = await open(path, 'a'); return handleFdClose(ftruncate(fd, len), fd.close); } diff --git a/test/parallel/test-fs-truncate-nonreadable.js b/test/parallel/test-fs-truncate-nonreadable.js new file mode 100644 index 00000000000000..5c93a7a2e82eef --- /dev/null +++ b/test/parallel/test-fs-truncate-nonreadable.js @@ -0,0 +1,48 @@ +'use strict'; + +const common = require('../common'); + +// This test ensures that truncate works on non-readable files + +const assert = require('assert'); +const fs = require('fs'); +const fsPromises = fs.promises; +const path = require('path'); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const expected = Buffer.from('good'); + +function checkContents(filepath) { + fs.chmodSync(filepath, 0o400); + assert.deepStrictEqual( + fs.readFileSync(filepath), + expected + ); +} + +(async () => { + const MODE = 0o200; + { + const filepath = path.resolve(tmpdir.path, 'promises.txt'); + fs.writeFileSync(filepath, 'good bad'); + fs.chmodSync(filepath, MODE); + await fsPromises.truncate(filepath, 4); + checkContents(filepath); + } + { + const filepath = path.resolve(tmpdir.path, 'callback.txt'); + fs.writeFileSync(filepath, 'good bad'); + fs.chmodSync(filepath, MODE); + fs.truncate(filepath, 4, common.mustSucceed(() => { + checkContents(filepath); + })); + } + { + const filepath = path.resolve(tmpdir.path, 'synchronous.txt'); + fs.writeFileSync(filepath, 'good bad'); + fs.chmodSync(filepath, MODE); + fs.truncateSync(filepath, 4); + checkContents(filepath); + } +})().then(common.mustCall()); diff --git a/test/parallel/test-fs-truncate.js b/test/parallel/test-fs-truncate.js index de9687ebb27f0c..b723da4686ef1e 100644 --- a/test/parallel/test-fs-truncate.js +++ b/test/parallel/test-fs-truncate.js @@ -242,17 +242,17 @@ function testFtruncate(cb) { } { - const file8 = path.resolve(tmp, 'non-existent-truncate-file.txt'); - const validateError = (err) => { - assert.strictEqual(file8, err.path); - assert.strictEqual( - err.message, - `ENOENT: no such file or directory, open '${file8}'`); - assert.strictEqual(err.code, 'ENOENT'); - assert.strictEqual(err.syscall, 'open'); - return true; - }; - fs.truncate(file8, 0, common.mustCall(validateError)); + const file8 = path.resolve(tmp, 'non-existent-truncate-file-1.txt'); + fs.truncate(file8, 0, common.mustSucceed(() => { + assert(fs.readFileSync(file8).equals(Buffer.from(''))); + })); +} + +{ + const file9 = path.resolve(tmp, 'non-existent-truncate-file-2.txt'); + fs.truncate(file9, 2, common.mustSucceed(() => { + assert(fs.readFileSync(file9).equals(Buffer.from('\u0000\u0000'))); + })); } ['', false, null, {}, []].forEach((input) => {