Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -1394,6 +1394,31 @@ function readdirRecursive(basePath, options, callback) {
read(context.pathsQueue[i++]);
}

function joinPath(base, name) {
if (BufferIsBuffer(base)) {
const baseStr = BufferToString(base);
const nameStr = BufferIsBuffer(name) ? BufferToString(name) : name;
return Buffer.from(pathModule.join(baseStr, nameStr));
}
return pathModule.join(base, name);
}

function relativePath(from, to) {
if (BufferIsBuffer(from)) {
const fromStr = BufferToString(from);
const toStr = BufferIsBuffer(to) ? BufferToString(to) : to;
return Buffer.from(pathModule.relative(fromStr, toStr));
}
return pathModule.relative(from, to);
}

function toPathString(path) {
if (BufferIsBuffer(path)) {
return BufferToString(path);
}
return path;
}

// Calling `readdir` with `withFileTypes=true`, the result is an array of arrays.
// The first array is the names, and the second array is the types.
// They are guaranteed to be the same length; hence, setting `length` to the length
Expand All @@ -1407,21 +1432,21 @@ function handleDirents({ result, currentPath, context }) {
for (let i = 0; i < length; i++) {
// Avoid excluding symlinks, as they are not directories.
// Refs: https://github.com/nodejs/node/issues/52663
const fullPath = pathModule.join(currentPath, names[i]);
const fullPath = joinPath(currentPath, names[i]);
const dirent = getDirent(currentPath, names[i], types[i]);
ArrayPrototypePush(context.readdirResults, dirent);

if (dirent.isDirectory() || binding.internalModuleStat(fullPath) === 1) {
if (dirent.isDirectory() || binding.internalModuleStat(toPathString(fullPath)) === 1) {
ArrayPrototypePush(context.pathsQueue, fullPath);
}
}
}

function handleFilePaths({ result, currentPath, context }) {
for (let i = 0; i < result.length; i++) {
const resultPath = pathModule.join(currentPath, result[i]);
const relativeResultPath = pathModule.relative(context.basePath, resultPath);
const stat = binding.internalModuleStat(resultPath);
const resultPath = joinPath(currentPath, result[i]);
const relativeResultPath = relativePath(context.basePath, resultPath);
const stat = binding.internalModuleStat(toPathString(resultPath));
ArrayPrototypePush(context.readdirResults, relativeResultPath);

if (stat === 1) {
Expand Down
33 changes: 29 additions & 4 deletions lib/internal/fs/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,31 @@ async function mkdir(path, options) {
);
}

function joinPath(base, name) {
if (BufferIsBuffer(base)) {
const baseStr = BufferToString(base);
const nameStr = BufferIsBuffer(name) ? BufferToString(name) : name;
return Buffer.from(pathModule.join(baseStr, nameStr));
}
return pathModule.join(base, name);
}

function relativePath(from, to) {
if (BufferIsBuffer(from)) {
const fromStr = BufferToString(from);
const toStr = BufferIsBuffer(to) ? BufferToString(to) : to;
return Buffer.from(pathModule.relative(fromStr, toStr));
}
return pathModule.relative(from, to);
}

function toPathString(path) {
if (BufferIsBuffer(path)) {
return BufferToString(path);
}
return path;
}

async function readdirRecursive(originalPath, options) {
const result = [];
const queue = [
Expand All @@ -896,7 +921,7 @@ async function readdirRecursive(originalPath, options) {
for (const dirent of getDirents(path, readdir)) {
ArrayPrototypePush(result, dirent);
if (dirent.isDirectory()) {
const direntPath = pathModule.join(path, dirent.name);
const direntPath = joinPath(path, dirent.name);
ArrayPrototypePush(queue, [
direntPath,
await PromisePrototypeThen(
Expand All @@ -917,11 +942,11 @@ async function readdirRecursive(originalPath, options) {
while (queue.length > 0) {
const { 0: path, 1: readdir } = ArrayPrototypePop(queue);
for (const ent of readdir) {
const direntPath = pathModule.join(path, ent);
const stat = binding.internalModuleStat(direntPath);
const direntPath = joinPath(path, ent);
const stat = binding.internalModuleStat(toPathString(direntPath));
ArrayPrototypePush(
result,
pathModule.relative(originalPath, direntPath),
relativePath(originalPath, direntPath),
);
if (stat === 1) {
ArrayPrototypePush(queue, [
Expand Down
15 changes: 0 additions & 15 deletions test/known_issues/test-fs-readdir-promise-recursive-with-buffer.js

This file was deleted.

15 changes: 0 additions & 15 deletions test/known_issues/test-fs-readdir-recursive-with-buffer.js

This file was deleted.

15 changes: 0 additions & 15 deletions test/known_issues/test-fs-readdir-sync-recursive-with-buffer.js

This file was deleted.

30 changes: 30 additions & 0 deletions test/parallel/test-fs-readdir-promise-recursive-with-buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict';

// Test that fs.promises.readdir works with Buffer paths in recursive mode.
// Refs: https://github.com/nodejs/node/issues/58892

const common = require('../common');
const tmpdir = require('../common/tmpdir');

const assert = require('node:assert');
const { readdir, mkdir, writeFile } = require('node:fs/promises');
const { join } = require('node:path');

async function runTest() {
tmpdir.refresh();

const subdir = join(tmpdir.path, 'subdir');
await mkdir(subdir);
await writeFile(join(tmpdir.path, 'file1.txt'), 'content1');
await writeFile(join(subdir, 'file2.txt'), 'content2');

const result1 = await readdir(Buffer.from(tmpdir.path), { recursive: true });
assert(Array.isArray(result1));
assert.strictEqual(result1.length, 3);

const result2 = await readdir(Buffer.from(tmpdir.path), { recursive: true, withFileTypes: true });
assert(Array.isArray(result2));
assert.strictEqual(result2.length, 3);
}

runTest().then(common.mustCall());
28 changes: 28 additions & 0 deletions test/parallel/test-fs-readdir-recursive-with-buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

// Test that readdir callback works with Buffer paths in recursive mode.
// Refs: https://github.com/nodejs/node/issues/58892

const common = require('../common');
const tmpdir = require('../common/tmpdir');

const assert = require('node:assert');
const { readdir, mkdirSync, writeFileSync } = require('node:fs');
const { join } = require('node:path');

tmpdir.refresh();

const subdir = join(tmpdir.path, 'subdir');
mkdirSync(subdir);
writeFileSync(join(tmpdir.path, 'file1.txt'), 'content1');
writeFileSync(join(subdir, 'file2.txt'), 'content2');

readdir(Buffer.from(tmpdir.path), { recursive: true }, common.mustSucceed((result) => {
assert(Array.isArray(result));
assert.strictEqual(result.length, 3);
}));

readdir(Buffer.from(tmpdir.path), { recursive: true, withFileTypes: true }, common.mustSucceed((result) => {
assert(Array.isArray(result));
assert.strictEqual(result.length, 3);
}));
26 changes: 26 additions & 0 deletions test/parallel/test-fs-readdir-sync-recursive-with-buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use strict';

// Test that readdirSync works with Buffer paths in recursive mode.
// Refs: https://github.com/nodejs/node/issues/58892

require('../common');
const tmpdir = require('../common/tmpdir');

const assert = require('node:assert');
const { readdirSync, mkdirSync, writeFileSync } = require('node:fs');
const { join } = require('node:path');

tmpdir.refresh();

const subdir = join(tmpdir.path, 'subdir');
mkdirSync(subdir);
writeFileSync(join(tmpdir.path, 'file1.txt'), 'content1');
writeFileSync(join(subdir, 'file2.txt'), 'content2');

const result1 = readdirSync(Buffer.from(tmpdir.path), { recursive: true });
assert(Array.isArray(result1));
assert.strictEqual(result1.length, 3);

const result2 = readdirSync(Buffer.from(tmpdir.path), { recursive: true, withFileTypes: true });
assert(Array.isArray(result2));
assert.strictEqual(result2.length, 3);
Loading