From e0edb5dd0d1ff6f0743647d450326705e394f6b0 Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Sun, 25 Aug 2019 19:58:38 +0200 Subject: [PATCH 01/19] wip --- lib/fs.js | 4 ++-- lib/internal/fs/utils.js | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/fs.js b/lib/fs.js index 5c7d907f5e546d..c330cbed2cdbdb 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1170,7 +1170,7 @@ function fchownSync(fd, uid, gid) { handleErrorFromBinding(ctx); } -function chown(path, uid, gid, callback) { +function chown(path, uid, gid, options, callback) { callback = makeCallback(callback); path = getValidatedPath(path); validateUint32(uid, 'uid'); @@ -1181,7 +1181,7 @@ function chown(path, uid, gid, callback) { binding.chown(pathModule.toNamespacedPath(path), uid, gid, req); } -function chownSync(path, uid, gid) { +function chownSync(path, uid, gid, options) { path = getValidatedPath(path); validateUint32(uid, 'uid'); validateUint32(gid, 'gid'); diff --git a/lib/internal/fs/utils.js b/lib/internal/fs/utils.js index fb060d23e66ffc..0772ce01cc0908 100644 --- a/lib/internal/fs/utils.js +++ b/lib/internal/fs/utils.js @@ -552,6 +552,11 @@ const validateRmdirOptions = hideStackFrames((options) => { return options; }); +// ex1. from: {uid: 1, gid: 1} +// ex2. preserveRoot: true +// ex3. noPreserveRoot: false +// ex4. reference: 'some/path' + module.exports = { assertEncoding, @@ -560,19 +565,19 @@ module.exports = { Dirent, getDirents, getOptions, + getStatsFromBinding, getValidatedPath, nullCheck, preprocessSymlinkDestination, realpathCacheKey: Symbol('realpathCacheKey'), - getStatsFromBinding, + Stats, stringToFlags, stringToSymlinkType, - Stats, toUnixTimestamp, validateBufferArray, validateOffsetLengthRead, validateOffsetLengthWrite, validatePath, validateRmdirOptions, - warnOnNonPortableTemplate + warnOnNonPortableTemplate, }; From d90aec55f2c670d8e7fac70f1c504788ac89d645 Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Sun, 25 Aug 2019 19:58:56 +0200 Subject: [PATCH 02/19] wip --- lib/internal/fs/utils.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/internal/fs/utils.js b/lib/internal/fs/utils.js index 0772ce01cc0908..b2df0e254cdc26 100644 --- a/lib/internal/fs/utils.js +++ b/lib/internal/fs/utils.js @@ -552,11 +552,31 @@ const validateRmdirOptions = hideStackFrames((options) => { return options; }); +// @TODO: Add additional options to match chown cli utility. // ex1. from: {uid: 1, gid: 1} // ex2. preserveRoot: true // ex3. noPreserveRoot: false // ex4. reference: 'some/path' +const defaultChownOptions = { + recursive: false, +}; + +const validateChownOptions = hideStackFrames((options) => { + if (options === undefined) + return defaultChownOptions; + if (options === null || typeof options !== 'object') + throw new ERR_INVALID_ARG_TYPE('options', 'object', options); + + options = { ...defaultRmdirOptions, ...options }; + + if (typeof options.recursive !== 'boolean') + throw new ERR_INVALID_ARG_TYPE('recursive', 'boolean', options.recursive); + + return options; + +}); + module.exports = { assertEncoding, @@ -575,6 +595,7 @@ module.exports = { stringToSymlinkType, toUnixTimestamp, validateBufferArray, + validateChownOptions, validateOffsetLengthRead, validateOffsetLengthWrite, validatePath, From fe0bafd8ee05d73beaf69db7628a357267187237 Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Tue, 27 Aug 2019 21:04:00 +0200 Subject: [PATCH 03/19] wip --- lib/fs.js | 15 +++++++++++++++ lib/internal/fs/utils.js | 7 +------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/fs.js b/lib/fs.js index c330cbed2cdbdb..d52021240222bc 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1185,9 +1185,24 @@ function chownSync(path, uid, gid, options) { path = getValidatedPath(path); validateUint32(uid, 'uid'); validateUint32(gid, 'gid'); + + const { recursive = false } = options; + const ctx = { path }; binding.chown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx); handleErrorFromBinding(ctx); + + if (!recursive) { return; } + + const nextPath = path.split(pathModule.sep); + + nextPath.pop(); + + if (!nextPath.length) { + return; + } + const pathToStr = nextPath.join(path.sep); + chownSync(pathToStr, uid, gid, options); } function utimes(path, atime, mtime, callback) { diff --git a/lib/internal/fs/utils.js b/lib/internal/fs/utils.js index b2df0e254cdc26..2497a039067e24 100644 --- a/lib/internal/fs/utils.js +++ b/lib/internal/fs/utils.js @@ -553,11 +553,6 @@ const validateRmdirOptions = hideStackFrames((options) => { }); // @TODO: Add additional options to match chown cli utility. -// ex1. from: {uid: 1, gid: 1} -// ex2. preserveRoot: true -// ex3. noPreserveRoot: false -// ex4. reference: 'some/path' - const defaultChownOptions = { recursive: false, }; @@ -568,7 +563,7 @@ const validateChownOptions = hideStackFrames((options) => { if (options === null || typeof options !== 'object') throw new ERR_INVALID_ARG_TYPE('options', 'object', options); - options = { ...defaultRmdirOptions, ...options }; + options = { ...defaultChownOptions, ...options }; if (typeof options.recursive !== 'boolean') throw new ERR_INVALID_ARG_TYPE('recursive', 'boolean', options.recursive); From 28b7c12166743d0d840959ad8ed791f0ed4f192c Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Wed, 28 Aug 2019 08:40:50 +0200 Subject: [PATCH 04/19] wip --- lib/fs.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/fs.js b/lib/fs.js index d52021240222bc..482752cd0976eb 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1176,9 +1176,12 @@ function chown(path, uid, gid, options, callback) { validateUint32(uid, 'uid'); validateUint32(gid, 'gid'); + const { recursive = false } = options; + const req = new FSReqCallback(); req.oncomplete = callback; binding.chown(pathModule.toNamespacedPath(path), uid, gid, req); + } function chownSync(path, uid, gid, options) { From 4ad2bbd87054e09bf1cf6dd76412bb30f78eb623 Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Wed, 28 Aug 2019 21:34:17 +0200 Subject: [PATCH 05/19] add test for chown rec --- lib/fs.js | 3 - test/parallel/test-fs-chown-recursive.js | 71 ++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-fs-chown-recursive.js diff --git a/lib/fs.js b/lib/fs.js index 482752cd0976eb..d52021240222bc 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1176,12 +1176,9 @@ function chown(path, uid, gid, options, callback) { validateUint32(uid, 'uid'); validateUint32(gid, 'gid'); - const { recursive = false } = options; - const req = new FSReqCallback(); req.oncomplete = callback; binding.chown(pathModule.toNamespacedPath(path), uid, gid, req); - } function chownSync(path, uid, gid, options) { diff --git a/test/parallel/test-fs-chown-recursive.js b/test/parallel/test-fs-chown-recursive.js new file mode 100644 index 00000000000000..b084ace8c0e8df --- /dev/null +++ b/test/parallel/test-fs-chown-recursive.js @@ -0,0 +1,71 @@ +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const { validateChownOptions } = require('internal/fs/utils'); +let count = 0; + +tmpdir.refresh(); + +function makeNonEmptyDirectory() { + const dirname = `chown-recursive-${count}`; + fs.mkdirSync(path.join(dirname, 'foo', 'bar', 'baz'), { recursive: true }); + fs.writeFileSync(path.join(dirname, 'text.txt'), 'hello', 'utf8'); + count++; + return dirname; +} + +// Test the synchronous version. +{ + const dir = makeNonEmptyDirectory(); + + // Recursive removal should succeed. + fs.chownSync(dir, 1, 1, { recursive: true }); + + // No error should occur if recursive and the directory does not exist. + fs.chownSync(dir, 1, 1, { recursive: true }); + + // Attempted removal should fail now because the directory is gone. + common.expectsError(() => fs.chownSync(dir), { syscall: 'chown' }); +} + +// Test input validation. +{ + const defaults = { + recursive: false + }; + const modified = { + recursive: true + }; + + assert.deepStrictEqual(validateChownOptions(), defaults); + assert.deepStrictEqual(validateChownOptions({}), defaults); + assert.deepStrictEqual(validateChownOptions(modified), modified); + assert.deepStrictEqual(validateChownOptions({ + }), { + recursive: false + }); + + [null, 'foo', 5, NaN].forEach((bad) => { + common.expectsError(() => { + validateChownOptions(bad); + }, { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: /^The "options" argument must be of type object\./ + }); + }); + + [undefined, null, 'foo', Infinity, function() {}].forEach((bad) => { + common.expectsError(() => { + validateChownOptions({ recursive: bad }); + }, { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: /^The "recursive" argument must be of type boolean\./ + }); + }); +} From 5dd2b85047e492d18cd14f5912781e33917603ec Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Fri, 30 Aug 2019 09:33:48 +0200 Subject: [PATCH 06/19] wip --- lib/internal/fs/chownrec.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lib/internal/fs/chownrec.js diff --git a/lib/internal/fs/chownrec.js b/lib/internal/fs/chownrec.js new file mode 100644 index 00000000000000..17b221420abf47 --- /dev/null +++ b/lib/internal/fs/chownrec.js @@ -0,0 +1,14 @@ +'use strict'; + +const { readdirSync, lstatSync, chownSync } = require('fs'); + +function _chownRecSync(path, uid, gid, options) { + const stats = lstatSync(path); + if (stats !== undefined && stats.isDirectory()) { + const files = readdirSync(path); + files.forEach((file) => + chownSync(path, uid, gid, options)); + } else { + chownSync(path, uid, gid, options); + } +} From 0c33fc998c70f1c4e10a04ed8ccb63c7b7c6d203 Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Sun, 1 Sep 2019 18:56:40 +0200 Subject: [PATCH 07/19] wip --- lib/fs.js | 24 +++++++++---------- .../fs/{chownrec.js => chown_recursive.js} | 8 +++++-- test/parallel/test-fs-chown-recursive.js | 16 ++++--------- 3 files changed, 23 insertions(+), 25 deletions(-) rename lib/internal/fs/{chownrec.js => chown_recursive.js} (55%) diff --git a/lib/fs.js b/lib/fs.js index d52021240222bc..54d1f59edfaaf1 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -103,6 +103,8 @@ let ReadStream; let WriteStream; let rimraf; let rimrafSync; +let chownRecursive; +let chownRecursiveSync; // These have to be separate because of how graceful-fs happens to do it's // monkeypatching. @@ -745,6 +747,11 @@ function lazyLoadRimraf() { ({ rimraf, rimrafSync } = require('internal/fs/rimraf')); } +function lazyLoadChownRecursive() { + if (chownRecursive === undefined) + ({ chownRecursive, chownRecursiveSync } = require('internal/fs/chown_recursive')); +} + function rmdir(path, options, callback) { if (typeof options === 'function') { callback = options; @@ -1188,21 +1195,14 @@ function chownSync(path, uid, gid, options) { const { recursive = false } = options; + if (recursive) { + lazyLoadChownRecursive(); + chownRecursiveSync(path, uid, gid, options); + } + const ctx = { path }; binding.chown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx); handleErrorFromBinding(ctx); - - if (!recursive) { return; } - - const nextPath = path.split(pathModule.sep); - - nextPath.pop(); - - if (!nextPath.length) { - return; - } - const pathToStr = nextPath.join(path.sep); - chownSync(pathToStr, uid, gid, options); } function utimes(path, atime, mtime, callback) { diff --git a/lib/internal/fs/chownrec.js b/lib/internal/fs/chown_recursive.js similarity index 55% rename from lib/internal/fs/chownrec.js rename to lib/internal/fs/chown_recursive.js index 17b221420abf47..2c02ea3323ceb9 100644 --- a/lib/internal/fs/chownrec.js +++ b/lib/internal/fs/chown_recursive.js @@ -2,13 +2,17 @@ const { readdirSync, lstatSync, chownSync } = require('fs'); -function _chownRecSync(path, uid, gid, options) { +function chownRecursiveSync(path, uid, gid, options) { const stats = lstatSync(path); if (stats !== undefined && stats.isDirectory()) { const files = readdirSync(path); files.forEach((file) => - chownSync(path, uid, gid, options)); + chownRecursiveSync(path, uid, gid, options)); } else { chownSync(path, uid, gid, options); } } + +function chownRecursive(path, uid, gid, options, callback) {} + +module.exports = { chownRecursiveSync, chownRecursive }; diff --git a/test/parallel/test-fs-chown-recursive.js b/test/parallel/test-fs-chown-recursive.js index b084ace8c0e8df..fd0650a0bff9b2 100644 --- a/test/parallel/test-fs-chown-recursive.js +++ b/test/parallel/test-fs-chown-recursive.js @@ -22,14 +22,8 @@ function makeNonEmptyDirectory() { { const dir = makeNonEmptyDirectory(); - // Recursive removal should succeed. + // Recursive chown should succeed. fs.chownSync(dir, 1, 1, { recursive: true }); - - // No error should occur if recursive and the directory does not exist. - fs.chownSync(dir, 1, 1, { recursive: true }); - - // Attempted removal should fail now because the directory is gone. - common.expectsError(() => fs.chownSync(dir), { syscall: 'chown' }); } // Test input validation. @@ -49,9 +43,9 @@ function makeNonEmptyDirectory() { recursive: false }); - [null, 'foo', 5, NaN].forEach((bad) => { + [null, 'foo', 5, NaN].forEach((badArg) => { common.expectsError(() => { - validateChownOptions(bad); + validateChownOptions(badArg); }, { code: 'ERR_INVALID_ARG_TYPE', type: TypeError, @@ -59,9 +53,9 @@ function makeNonEmptyDirectory() { }); }); - [undefined, null, 'foo', Infinity, function() {}].forEach((bad) => { + [undefined, null, 'foo', Infinity, function() {}].forEach((badValue) => { common.expectsError(() => { - validateChownOptions({ recursive: bad }); + validateChownOptions({ recursive: badValue }); }, { code: 'ERR_INVALID_ARG_TYPE', type: TypeError, From f89d2ae2a58e9c7a1bea28a534de3c4a741e0381 Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Sun, 1 Sep 2019 20:09:59 +0200 Subject: [PATCH 08/19] wip --- lib/internal/fs/chown_recursive.js | 92 +++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/lib/internal/fs/chown_recursive.js b/lib/internal/fs/chown_recursive.js index 2c02ea3323ceb9..6ffb64e0ca0c7b 100644 --- a/lib/internal/fs/chown_recursive.js +++ b/lib/internal/fs/chown_recursive.js @@ -1,6 +1,20 @@ +/** + * Chown recursively similar to chown -R + */ 'use strict'; -const { readdirSync, lstatSync, chownSync } = require('fs'); +const { + readdir, + readdirSync, + lstat, + lstatSync, + chown, + chownSync +} = require('fs'); + +const { join } = require('path'); +const notEmptyErrorCodes = new Set(['ENOTEMPTY', 'EEXIST']); +const { setTimeout } = require('timers'); function chownRecursiveSync(path, uid, gid, options) { const stats = lstatSync(path); @@ -13,6 +27,80 @@ function chownRecursiveSync(path, uid, gid, options) { } } -function chownRecursive(path, uid, gid, options, callback) {} + +function _chownChildren(path, uid, gid, options, callback) { + readdir(path, (err, files) => { + if (err) + return callback(err); + + let numFiles = files.length; + + if (numFiles === 0) + return chown(path, uid, gid, callback); + + let done = false; + + files.forEach((child) => { + chownRecursive(join(path, child), uid, gid, options, (err) => { + if (done) + return; + + if (err) { + done = true; + return callback(err); + } + + numFiles--; + if (numFiles === 0) + chown(path, uid, gid, callback); + }); + }); + }); +} + +function _chown(path, uid, gid, options, originalErr, callback) { + chown(path, (err) => { + if (err) { + if (notEmptyErrorCodes.has(err.code)) + return _chownChildren(path, uid, gid, options, callback); + if (err.code === 'ENOTDIR') + return callback(originalErr); + } + callback(err); + }); +} + +function _chownRecursive(path, uid, gid, options, callback) { + lstat(path, (err, stats) => { + if (err) { + if (err.code === 'ENOENT') + return callback(null); + } else if (stats.isDirectory()) { + return _chown(path, uid, gid, options, err, callback); + } + }); +} + +function chownRecursive(path, uid, gid, options, callback) { + let timeout = 0; // For EMFILE handling. + let busyTries = 0; + _chownRecursive(path, uid, gid, options, function CB(err) { + if (err) { + if ((err.code === 'EBUSY' || err.code === 'ENOTEMPTY' || + err.code === 'EPERM') && busyTries < options.maxBusyTries) { + busyTries++; + return setTimeout(_chownRecursive, busyTries * 100, path, options, CB); + } + + if (err.code === 'EMFILE' && timeout < options.emfileWait) + return setTimeout(_chownRecursive, timeout++, path, options, CB); + + // The file is already gone. + if (err.code === 'ENOENT') + err = null; + } + callback(err); + })}; + module.exports = { chownRecursiveSync, chownRecursive }; From 7c4d015e8c23f1d21e36ba600de0f00efd91d451 Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Sun, 1 Sep 2019 20:11:20 +0200 Subject: [PATCH 09/19] wip --- lib/internal/fs/chown_recursive.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/internal/fs/chown_recursive.js b/lib/internal/fs/chown_recursive.js index 6ffb64e0ca0c7b..8ccd0739851a47 100644 --- a/lib/internal/fs/chown_recursive.js +++ b/lib/internal/fs/chown_recursive.js @@ -94,13 +94,10 @@ function chownRecursive(path, uid, gid, options, callback) { if (err.code === 'EMFILE' && timeout < options.emfileWait) return setTimeout(_chownRecursive, timeout++, path, options, CB); - - // The file is already gone. - if (err.code === 'ENOENT') - err = null; } callback(err); - })}; + }); +} module.exports = { chownRecursiveSync, chownRecursive }; From e6c215d316d78480ff301b36d1c91f11984758e0 Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Sun, 1 Sep 2019 20:15:42 +0200 Subject: [PATCH 10/19] update fs.js --- lib/fs.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/fs.js b/lib/fs.js index 54d1f59edfaaf1..04d3cf240edc98 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -76,8 +76,9 @@ const { validateOffsetLengthRead, validateOffsetLengthWrite, validatePath, + validateChownOptions, validateRmdirOptions, - warnOnNonPortableTemplate + warnOnNonPortableTemplate, } = require('internal/fs/utils'); const { CHAR_FORWARD_SLASH, @@ -749,7 +750,8 @@ function lazyLoadRimraf() { function lazyLoadChownRecursive() { if (chownRecursive === undefined) - ({ chownRecursive, chownRecursiveSync } = require('internal/fs/chown_recursive')); + ({ chownRecursive, + chownRecursiveSync } = require('internal/fs/chown_recursive')); } function rmdir(path, options, callback) { @@ -1183,6 +1185,15 @@ function chown(path, uid, gid, options, callback) { validateUint32(uid, 'uid'); validateUint32(gid, 'gid'); + callback = makeCallback(callback); + path = pathModule.toNamespacedPath(getValidatedPath(path)); + options = validateChownOptions(options); + + if (options.recursive) { + lazyLoadChownRecursive(); + return chownRecursive(path, uid, gid, options, callback); + } + const req = new FSReqCallback(); req.oncomplete = callback; binding.chown(pathModule.toNamespacedPath(path), uid, gid, req); @@ -1197,7 +1208,7 @@ function chownSync(path, uid, gid, options) { if (recursive) { lazyLoadChownRecursive(); - chownRecursiveSync(path, uid, gid, options); + return chownRecursiveSync(path, uid, gid, options); } const ctx = { path }; From 29d9a1522dae72e84068c226ae19bc3f5c1fed3b Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Sun, 1 Sep 2019 20:26:23 +0200 Subject: [PATCH 11/19] update docs remove some rimraf retries --- doc/api/fs.md | 16 ++++++++++++++-- lib/internal/fs/chown_recursive.js | 10 ++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/doc/api/fs.md b/doc/api/fs.md index 7e8266a90ec63a..5c3dc153eedc2e 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -1318,7 +1318,7 @@ this API: [`fs.chmod()`][]. See also: chmod(2). -## fs.chown(path, uid, gid, callback) +## fs.chown(path, uid, gid[, options], callback) <!-- YAML added: v0.1.97 changes: @@ -1336,9 +1336,15 @@ changes: it will emit a deprecation warning with id DEP0013. --> +> Stability: 1 - Recursive chown is experimental. + * `path` {string|Buffer|URL} * `uid` {integer} * `gid` {integer} +* `options` {object} + * `recursive` {boolean} If `true`, perform a recursive directory chown. In + recursive mode, errors are not reported if `path` does not exist, and + operations are retried on failure. **Default:** `false`. * `callback` {Function} * `err` {Error} @@ -1347,7 +1353,7 @@ possible exception are given to the completion callback. See also: chown(2). -## fs.chownSync(path, uid, gid) +## fs.chownSync(path, uid, gid[, options]) <!-- YAML added: v0.1.97 changes: @@ -1357,9 +1363,15 @@ changes: protocol. Support is currently still *experimental*. --> +> Stability: 1 - Recursive removal is experimental. + * `path` {string|Buffer|URL} * `uid` {integer} * `gid` {integer} +* `options` {Object} + * `recursive` {boolean} If `true`, perform a recursive directory chown. In + recursive mode, errors are not reported if `path` does not exist, and + operations are retried on failure. **Default:** `false`. Synchronously changes owner and group of a file. Returns `undefined`. This is the synchronous version of [`fs.chown()`][]. diff --git a/lib/internal/fs/chown_recursive.js b/lib/internal/fs/chown_recursive.js index 8ccd0739851a47..9412dff7a665c0 100644 --- a/lib/internal/fs/chown_recursive.js +++ b/lib/internal/fs/chown_recursive.js @@ -1,5 +1,6 @@ /** * Chown recursively similar to chown -R + * code is similar to rimraf except less functionality */ 'use strict'; @@ -83,16 +84,9 @@ function _chownRecursive(path, uid, gid, options, callback) { function chownRecursive(path, uid, gid, options, callback) { let timeout = 0; // For EMFILE handling. - let busyTries = 0; _chownRecursive(path, uid, gid, options, function CB(err) { if (err) { - if ((err.code === 'EBUSY' || err.code === 'ENOTEMPTY' || - err.code === 'EPERM') && busyTries < options.maxBusyTries) { - busyTries++; - return setTimeout(_chownRecursive, busyTries * 100, path, options, CB); - } - - if (err.code === 'EMFILE' && timeout < options.emfileWait) + if (err.code === 'EMFILE') return setTimeout(_chownRecursive, timeout++, path, options, CB); } callback(err); From 620d27211e350de465e72e1903f3cbbaedc8950a Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Mon, 2 Sep 2019 08:55:14 +0200 Subject: [PATCH 12/19] update empty codes --- lib/internal/fs/chown_recursive.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/internal/fs/chown_recursive.js b/lib/internal/fs/chown_recursive.js index 9412dff7a665c0..36579457feabae 100644 --- a/lib/internal/fs/chown_recursive.js +++ b/lib/internal/fs/chown_recursive.js @@ -1,7 +1,4 @@ -/** - * Chown recursively similar to chown -R - * code is similar to rimraf except less functionality - */ +// Chown recursively similar to chown -R 'use strict'; const { @@ -14,7 +11,7 @@ const { } = require('fs'); const { join } = require('path'); -const notEmptyErrorCodes = new Set(['ENOTEMPTY', 'EEXIST']); +const notEmptyErrorCodes = new Set(['EEXIST']); const { setTimeout } = require('timers'); function chownRecursiveSync(path, uid, gid, options) { From 3bbd03e30283c32e674b409a6ce65a1e765e1914 Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Mon, 2 Sep 2019 21:02:19 +0200 Subject: [PATCH 13/19] create directory structure --- test/parallel/test-fs-chown-recursive.js | 33 ++++++++++++++++++------ 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/test/parallel/test-fs-chown-recursive.js b/test/parallel/test-fs-chown-recursive.js index fd0650a0bff9b2..1873bf37935f65 100644 --- a/test/parallel/test-fs-chown-recursive.js +++ b/test/parallel/test-fs-chown-recursive.js @@ -6,21 +6,38 @@ const assert = require('assert'); const fs = require('fs'); const path = require('path'); const { validateChownOptions } = require('internal/fs/utils'); -let count = 0; tmpdir.refresh(); -function makeNonEmptyDirectory() { - const dirname = `chown-recursive-${count}`; - fs.mkdirSync(path.join(dirname, 'foo', 'bar', 'baz'), { recursive: true }); - fs.writeFileSync(path.join(dirname, 'text.txt'), 'hello', 'utf8'); - count++; - return dirname; +/* +foo +|_ bar +| |_ file1.test +|_ baz +| |_ file2.test +|_ bax + |_ foo + |_ file3.test +*/ +function makeDirectories() { + const dirname = 'chown-recursive'; + + const foobarPath = path.join(dirname, 'foo', 'bar'); + fs.mkdirSync(foobarPath, { recursive: true }); + fs.writeFileSync(path.join(foobarPath, 'file1.test', 'file1')); + + const foobazPath = path.join(dirname, 'foo', 'baz'); + fs.mkdirSync(foobazPath, { recursive: true }); + fs.writeFileSync(path.join(foobazPath, 'file2.test', 'file1')); + + const foobaxPath = path.join(dirname, 'foo', 'bax', 'foo'); + fs.mkdirSync(foobaxPath, { recursive: true }); + fs.writeFileSync(path.join(foobaxPath, 'file3.test', 'file3')); } // Test the synchronous version. { - const dir = makeNonEmptyDirectory(); + const dir = makeDirectories(); // Recursive chown should succeed. fs.chownSync(dir, 1, 1, { recursive: true }); From f86104d11d54fbc53f692f08e8e90200da561841 Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Mon, 2 Sep 2019 21:38:37 +0200 Subject: [PATCH 14/19] update tests --- test/parallel/test-fs-chown-recursive.js | 57 +++++++++++++++++------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/test/parallel/test-fs-chown-recursive.js b/test/parallel/test-fs-chown-recursive.js index 1873bf37935f65..5a655cbc3b3527 100644 --- a/test/parallel/test-fs-chown-recursive.js +++ b/test/parallel/test-fs-chown-recursive.js @@ -9,16 +9,17 @@ const { validateChownOptions } = require('internal/fs/utils'); tmpdir.refresh(); -/* -foo -|_ bar -| |_ file1.test -|_ baz -| |_ file2.test -|_ bax - |_ foo - |_ file3.test -*/ +const filePaths = [ + 'foo/bar/file1.test', + 'foo/bar', + 'foo/baz/file2.test', + 'foo/baz', + 'foo/bax/file3.test', + 'foo/bax', + 'foo/file4.test', + 'foo' +]; + function makeDirectories() { const dirname = 'chown-recursive'; @@ -35,13 +36,6 @@ function makeDirectories() { fs.writeFileSync(path.join(foobaxPath, 'file3.test', 'file3')); } -// Test the synchronous version. -{ - const dir = makeDirectories(); - - // Recursive chown should succeed. - fs.chownSync(dir, 1, 1, { recursive: true }); -} // Test input validation. { @@ -80,3 +74,32 @@ function makeDirectories() { }); }); } + +// Test the synchronous version. +{ + const dir = makeDirectories(); + + // Recursive chown should succeed. + fs.chownSync(dir, 1, 1, { recursive: true }); + + filePaths.forEach((pathToCheck) => { + const stat = fs.lstatSync(pathToCheck); + assert.ok(stat.uid === 1, `uid for ${pathToCheck} should equal 1`); + assert.ok(stat.gid === 1, `gid for ${pathToCheck} should equal 1`); + }); + + fs.rmdirSync('foo'); + +} + +// test async version. +{ + makeDirectories(); + fs.chown(tmpdir.path, 1, 1, { recursive: true }, common.mustCall(() => { + filePaths.forEach((pathToCheck) => { + const stat = fs.lstatSync(pathToCheck); + assert.ok(stat.uid === 1, `uid for ${pathToCheck} should equal 1`); + assert.ok(stat.gid === 1, `gid for ${pathToCheck} should equal 1`); + }); + }, 1)); +} From 12435526b68c68fd5a3fc8b516e739c75fee3154 Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Mon, 2 Sep 2019 21:43:19 +0200 Subject: [PATCH 15/19] renamed to chownR and chownRSync, removed comments --- lib/fs.js | 20 ++++++++++---------- lib/internal/fs/chown_recursive.js | 18 ++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/lib/fs.js b/lib/fs.js index 04d3cf240edc98..9e5822eff167d8 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -104,8 +104,8 @@ let ReadStream; let WriteStream; let rimraf; let rimrafSync; -let chownRecursive; -let chownRecursiveSync; +let chownR; +let chownRSync; // These have to be separate because of how graceful-fs happens to do it's // monkeypatching. @@ -748,10 +748,10 @@ function lazyLoadRimraf() { ({ rimraf, rimrafSync } = require('internal/fs/rimraf')); } -function lazyLoadChownRecursive() { - if (chownRecursive === undefined) - ({ chownRecursive, - chownRecursiveSync } = require('internal/fs/chown_recursive')); +function lazyLoadChownR() { + if (chownR === undefined) + ({ chownR, + chownRSync } = require('internal/fs/chown_recursive')); } function rmdir(path, options, callback) { @@ -1190,8 +1190,8 @@ function chown(path, uid, gid, options, callback) { options = validateChownOptions(options); if (options.recursive) { - lazyLoadChownRecursive(); - return chownRecursive(path, uid, gid, options, callback); + lazyLoadChownR(); + return chownR(path, uid, gid, options, callback); } const req = new FSReqCallback(); @@ -1207,8 +1207,8 @@ function chownSync(path, uid, gid, options) { const { recursive = false } = options; if (recursive) { - lazyLoadChownRecursive(); - return chownRecursiveSync(path, uid, gid, options); + lazyLoadChownR(); + return chownRSync(path, uid, gid, options); } const ctx = { path }; diff --git a/lib/internal/fs/chown_recursive.js b/lib/internal/fs/chown_recursive.js index 36579457feabae..34326fd70abe4c 100644 --- a/lib/internal/fs/chown_recursive.js +++ b/lib/internal/fs/chown_recursive.js @@ -1,4 +1,3 @@ -// Chown recursively similar to chown -R 'use strict'; const { @@ -14,18 +13,17 @@ const { join } = require('path'); const notEmptyErrorCodes = new Set(['EEXIST']); const { setTimeout } = require('timers'); -function chownRecursiveSync(path, uid, gid, options) { +function chownRSync(path, uid, gid, options) { const stats = lstatSync(path); if (stats !== undefined && stats.isDirectory()) { const files = readdirSync(path); files.forEach((file) => - chownRecursiveSync(path, uid, gid, options)); + chownRSync(path, uid, gid, options)); } else { chownSync(path, uid, gid, options); } } - function _chownChildren(path, uid, gid, options, callback) { readdir(path, (err, files) => { if (err) @@ -39,7 +37,7 @@ function _chownChildren(path, uid, gid, options, callback) { let done = false; files.forEach((child) => { - chownRecursive(join(path, child), uid, gid, options, (err) => { + chownR(join(path, child), uid, gid, options, (err) => { if (done) return; @@ -68,7 +66,7 @@ function _chown(path, uid, gid, options, originalErr, callback) { }); } -function _chownRecursive(path, uid, gid, options, callback) { +function _chownR(path, uid, gid, options, callback) { lstat(path, (err, stats) => { if (err) { if (err.code === 'ENOENT') @@ -79,16 +77,16 @@ function _chownRecursive(path, uid, gid, options, callback) { }); } -function chownRecursive(path, uid, gid, options, callback) { +function chownR(path, uid, gid, options, callback) { let timeout = 0; // For EMFILE handling. - _chownRecursive(path, uid, gid, options, function CB(err) { + _chownR(path, uid, gid, options, function CB(err) { if (err) { if (err.code === 'EMFILE') - return setTimeout(_chownRecursive, timeout++, path, options, CB); + return setTimeout(_chownR, timeout++, path, options, CB); } callback(err); }); } -module.exports = { chownRecursiveSync, chownRecursive }; +module.exports = { chownRSync, chownR }; From 2ecbf6e071acc4d20ea818ff21b5c10d43017ff4 Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Tue, 3 Sep 2019 21:51:28 +0200 Subject: [PATCH 16/19] update test --- test/parallel/test-fs-chown-recursive.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/parallel/test-fs-chown-recursive.js b/test/parallel/test-fs-chown-recursive.js index 5a655cbc3b3527..3442082fad3933 100644 --- a/test/parallel/test-fs-chown-recursive.js +++ b/test/parallel/test-fs-chown-recursive.js @@ -25,15 +25,17 @@ function makeDirectories() { const foobarPath = path.join(dirname, 'foo', 'bar'); fs.mkdirSync(foobarPath, { recursive: true }); - fs.writeFileSync(path.join(foobarPath, 'file1.test', 'file1')); + fs.writeFileSync(path.join(foobarPath, 'file1.test'), 'file1'); const foobazPath = path.join(dirname, 'foo', 'baz'); fs.mkdirSync(foobazPath, { recursive: true }); - fs.writeFileSync(path.join(foobazPath, 'file2.test', 'file1')); + fs.writeFileSync(path.join(foobazPath, 'file2.test'), 'file1'); const foobaxPath = path.join(dirname, 'foo', 'bax', 'foo'); fs.mkdirSync(foobaxPath, { recursive: true }); - fs.writeFileSync(path.join(foobaxPath, 'file3.test', 'file3')); + fs.writeFileSync(path.join(foobaxPath, 'file3.test'), 'file3'); + + return dirname; } From c6b7808876e8208582042026ac5153b08365c84d Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Wed, 4 Sep 2019 20:29:13 +0200 Subject: [PATCH 17/19] changes - include in node.gyp - simplify test --- lib/fs.js | 4 +- node.gyp | 1 + test/parallel/test-fs-chown-recursive.js | 61 +++++++++++------------- 3 files changed, 29 insertions(+), 37 deletions(-) diff --git a/lib/fs.js b/lib/fs.js index 9e5822eff167d8..b6cd615b5b7f69 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1204,9 +1204,7 @@ function chownSync(path, uid, gid, options) { validateUint32(uid, 'uid'); validateUint32(gid, 'gid'); - const { recursive = false } = options; - - if (recursive) { + if (options.recursive) { lazyLoadChownR(); return chownRSync(path, uid, gid, options); } diff --git a/node.gyp b/node.gyp index 1d45f5117144b0..8af9cfe36d6c59 100644 --- a/node.gyp +++ b/node.gyp @@ -125,6 +125,7 @@ 'lib/internal/fs/promises.js', 'lib/internal/fs/read_file_context.js', 'lib/internal/fs/rimraf.js', + 'lib/internal/fs/chown_recursive.js', 'lib/internal/fs/streams.js', 'lib/internal/fs/sync_write_stream.js', 'lib/internal/fs/utils.js', diff --git a/test/parallel/test-fs-chown-recursive.js b/test/parallel/test-fs-chown-recursive.js index 3442082fad3933..6b23cc7b372845 100644 --- a/test/parallel/test-fs-chown-recursive.js +++ b/test/parallel/test-fs-chown-recursive.js @@ -9,33 +9,26 @@ const { validateChownOptions } = require('internal/fs/utils'); tmpdir.refresh(); -const filePaths = [ +const dirname = 'chown-recursive'; + +const paths = [ 'foo/bar/file1.test', 'foo/bar', - 'foo/baz/file2.test', - 'foo/baz', - 'foo/bax/file3.test', - 'foo/bax', - 'foo/file4.test', + 'foo/file2.test', 'foo' ]; -function makeDirectories() { - const dirname = 'chown-recursive'; - - const foobarPath = path.join(dirname, 'foo', 'bar'); - fs.mkdirSync(foobarPath, { recursive: true }); - fs.writeFileSync(path.join(foobarPath, 'file1.test'), 'file1'); +const expectUID = 1; +const expectGID = 1; - const foobazPath = path.join(dirname, 'foo', 'baz'); - fs.mkdirSync(foobazPath, { recursive: true }); - fs.writeFileSync(path.join(foobazPath, 'file2.test'), 'file1'); +const mainPath = path.join(tmpdir.path, dirname); +const fooPath = path.join(mainPath, 'foo'); - const foobaxPath = path.join(dirname, 'foo', 'bax', 'foo'); - fs.mkdirSync(foobaxPath, { recursive: true }); - fs.writeFileSync(path.join(foobaxPath, 'file3.test'), 'file3'); - - return dirname; +function makeDirectories() { + fs.mkdirSync(fooPath, { recursive: true }); + fs.mkdirSync(path.join(fooPath, 'bar')); + fs.writeFileSync(path.join(fooPath, 'bar', 'file1.test'), 'file1'); + fs.writeFileSync(path.join(fooPath, 'file2.test'), 'file2'); } @@ -79,29 +72,29 @@ function makeDirectories() { // Test the synchronous version. { - const dir = makeDirectories(); + makeDirectories(); // Recursive chown should succeed. - fs.chownSync(dir, 1, 1, { recursive: true }); - - filePaths.forEach((pathToCheck) => { - const stat = fs.lstatSync(pathToCheck); - assert.ok(stat.uid === 1, `uid for ${pathToCheck} should equal 1`); - assert.ok(stat.gid === 1, `gid for ${pathToCheck} should equal 1`); + fs.chownSync(fooPath, expectUID, expectGID, { recursive: true }); + + paths.forEach((p) => { + const stat = fs.lstatSync(path.join(fooPath, p)); + assert.ok(stat.uid === expectUID, + `uid for ${p} should equal ${expectUID}`); + assert.ok(stat.gid === expectGID, + `gid for ${p} should equal ${expectGID}`); }); - - fs.rmdirSync('foo'); - } - +/* // test async version. { makeDirectories(); - fs.chown(tmpdir.path, 1, 1, { recursive: true }, common.mustCall(() => { + fs.chown(tmpdir.path, expectUID, expectGID, { recursive: true }, common.mustCall(() => { filePaths.forEach((pathToCheck) => { const stat = fs.lstatSync(pathToCheck); - assert.ok(stat.uid === 1, `uid for ${pathToCheck} should equal 1`); - assert.ok(stat.gid === 1, `gid for ${pathToCheck} should equal 1`); + assert.ok(stat.uid === expectUID, `uid for ${pathToCheck} should equal ${expectUID}`); + assert.ok(stat.gid === expectGID, `gid for ${pathToCheck} should equal ${expectGID}`); }); }, 1)); } +*/ From 9fce609593f536ed9aa647f56440f56ab974d37f Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Wed, 4 Sep 2019 21:12:23 +0200 Subject: [PATCH 18/19] wip pass correct options update asserts --- lib/fs.js | 2 ++ lib/internal/fs/chown_recursive.js | 24 +++++++++++++++++++----- test/parallel/test-fs-chown-recursive.js | 14 +++++++------- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/lib/fs.js b/lib/fs.js index b6cd615b5b7f69..61e8c7d8157da4 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1204,6 +1204,8 @@ function chownSync(path, uid, gid, options) { validateUint32(uid, 'uid'); validateUint32(gid, 'gid'); + options = validateChownOptions(options); + if (options.recursive) { lazyLoadChownR(); return chownRSync(path, uid, gid, options); diff --git a/lib/internal/fs/chown_recursive.js b/lib/internal/fs/chown_recursive.js index 34326fd70abe4c..1ce90e30ba2e9a 100644 --- a/lib/internal/fs/chown_recursive.js +++ b/lib/internal/fs/chown_recursive.js @@ -9,21 +9,35 @@ const { chownSync } = require('fs'); -const { join } = require('path'); +const { join, resolve } = require('path'); const notEmptyErrorCodes = new Set(['EEXIST']); const { setTimeout } = require('timers'); function chownRSync(path, uid, gid, options) { const stats = lstatSync(path); if (stats !== undefined && stats.isDirectory()) { - const files = readdirSync(path); - files.forEach((file) => - chownRSync(path, uid, gid, options)); + const childrenPaths = readdirSync(path); + childrenPaths.forEach((childPath) => + _chownRChildrenSync(path, childPath, uid, gid, options)); } else { - chownSync(path, uid, gid, options); + chownSync(path, uid, gid); } } +function _chownRChildrenSync(path, childPath, uid, gid, options) { + if (typeof childPath === 'string') { + const stats = lstatSync(resolve(path, childPath)); + stats.name = childPath; + childPath = stats; + } + + if (childPath.isDirectory()) { + chownRSync(resolve(path, childPath.name), uid, gid, options); + } + + chownSync(path, uid, gid); +} + function _chownChildren(path, uid, gid, options, callback) { readdir(path, (err, files) => { if (err) diff --git a/test/parallel/test-fs-chown-recursive.js b/test/parallel/test-fs-chown-recursive.js index 6b23cc7b372845..b69c20193dd68c 100644 --- a/test/parallel/test-fs-chown-recursive.js +++ b/test/parallel/test-fs-chown-recursive.js @@ -12,9 +12,9 @@ tmpdir.refresh(); const dirname = 'chown-recursive'; const paths = [ - 'foo/bar/file1.test', - 'foo/bar', - 'foo/file2.test', + 'bar/file1.test', + 'bar', + 'file2.test', 'foo' ]; @@ -79,10 +79,10 @@ function makeDirectories() { paths.forEach((p) => { const stat = fs.lstatSync(path.join(fooPath, p)); - assert.ok(stat.uid === expectUID, - `uid for ${p} should equal ${expectUID}`); - assert.ok(stat.gid === expectGID, - `gid for ${p} should equal ${expectGID}`); + assert.strictEqual(stat.uid, expectUID, + `uid for ${p} should equal ${expectUID}`); + assert.strictEqual(stat.gid, expectGID, + `gid for ${p} should equal ${expectGID}`); }); } /* From 173cd605ad36d4cfe5dfbb6e77f5309700fb8a40 Mon Sep 17 00:00:00 2001 From: Giorgos Ntemiris <ntemirisgiorgos3@gmail.com> Date: Sun, 8 Sep 2019 18:20:11 +0200 Subject: [PATCH 19/19] wip --- lib/fs.js | 4 +-- lib/internal/fs/chown_recursive.js | 4 +-- test/parallel/test-fs-chown-recursive.js | 33 +++++++++++++++++------- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/lib/fs.js b/lib/fs.js index 61e8c7d8157da4..9c6a1e1f878bf6 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1191,7 +1191,7 @@ function chown(path, uid, gid, options, callback) { if (options.recursive) { lazyLoadChownR(); - return chownR(path, uid, gid, options, callback); + chownR(path, uid, gid, options, callback); } const req = new FSReqCallback(); @@ -1208,7 +1208,7 @@ function chownSync(path, uid, gid, options) { if (options.recursive) { lazyLoadChownR(); - return chownRSync(path, uid, gid, options); + chownRSync(path, uid, gid, options); } const ctx = { path }; diff --git a/lib/internal/fs/chown_recursive.js b/lib/internal/fs/chown_recursive.js index 1ce90e30ba2e9a..540af8a758afbf 100644 --- a/lib/internal/fs/chown_recursive.js +++ b/lib/internal/fs/chown_recursive.js @@ -16,7 +16,7 @@ const { setTimeout } = require('timers'); function chownRSync(path, uid, gid, options) { const stats = lstatSync(path); if (stats !== undefined && stats.isDirectory()) { - const childrenPaths = readdirSync(path); + const childrenPaths = readdirSync(path, { withFileTypes: true }); childrenPaths.forEach((childPath) => _chownRChildrenSync(path, childPath, uid, gid, options)); } else { @@ -35,7 +35,7 @@ function _chownRChildrenSync(path, childPath, uid, gid, options) { chownRSync(resolve(path, childPath.name), uid, gid, options); } - chownSync(path, uid, gid); + chownSync(resolve(path, childPath.name), uid, gid); } function _chownChildren(path, uid, gid, options, callback) { diff --git a/test/parallel/test-fs-chown-recursive.js b/test/parallel/test-fs-chown-recursive.js index b69c20193dd68c..2c8a663b0ab55b 100644 --- a/test/parallel/test-fs-chown-recursive.js +++ b/test/parallel/test-fs-chown-recursive.js @@ -7,28 +7,37 @@ const fs = require('fs'); const path = require('path'); const { validateChownOptions } = require('internal/fs/utils'); -tmpdir.refresh(); - const dirname = 'chown-recursive'; +/** + * Temporary dir structure + * + * .tmp.0 + * └── chown-recursive + * └── foo + * ├── bar + * │ └── file1.test + * └── file2.test + */ + const paths = [ 'bar/file1.test', 'bar', 'file2.test', - 'foo' + '.' // refers to foo ]; const expectUID = 1; const expectGID = 1; const mainPath = path.join(tmpdir.path, dirname); -const fooPath = path.join(mainPath, 'foo'); +const fooPath = path.resolve(mainPath, 'foo'); function makeDirectories() { fs.mkdirSync(fooPath, { recursive: true }); - fs.mkdirSync(path.join(fooPath, 'bar')); - fs.writeFileSync(path.join(fooPath, 'bar', 'file1.test'), 'file1'); - fs.writeFileSync(path.join(fooPath, 'file2.test'), 'file2'); + fs.mkdirSync(path.resolve(fooPath, 'bar')); + fs.writeFileSync(path.resolve(fooPath, 'bar', 'file1.test'), 'file1'); + fs.writeFileSync(path.resolve(fooPath, 'file2.test'), 'file2'); } @@ -72,17 +81,21 @@ function makeDirectories() { // Test the synchronous version. { + makeDirectories(); // Recursive chown should succeed. fs.chownSync(fooPath, expectUID, expectGID, { recursive: true }); paths.forEach((p) => { - const stat = fs.lstatSync(path.join(fooPath, p)); + const pathToEvaluate = path.join(fooPath, p); + const stat = fs.lstatSync(pathToEvaluate); assert.strictEqual(stat.uid, expectUID, - `uid for ${p} should equal ${expectUID}`); + `uid for ${p} should equal ${expectUID} \ + for path ${pathToEvaluate}`); assert.strictEqual(stat.gid, expectGID, - `gid for ${p} should equal ${expectGID}`); + `gid for ${p} should equal ${expectGID} \ + for path ${pathToEvaluate}`); }); } /*