diff --git a/src/__tests__/union.test.ts b/src/__tests__/union.test.ts index b50e1a08..5174bcb2 100644 --- a/src/__tests__/union.test.ts +++ b/src/__tests__/union.test.ts @@ -161,6 +161,19 @@ describe('union', () => { expect(ufs.readdirSync('/bar')).toEqual(['baz', 'qux']); }); + // regression test for https://github.com/streamich/unionfs/issues/782 + it('does not throw error when directory is empty and other fs fails', () => { + const vol = Volume.fromJSON({}); + const vol2 = Volume.fromJSON({ + '/bar': null, + }); + + const ufs = new Union(); + ufs.use(vol as any); + ufs.use(vol2 as any); + expect(ufs.readdirSync('/bar')).toEqual([]); + }); + it('honors the withFileTypes: true option', () => { const vol = Volume.fromJSON({ '/foo/bar': 'bar', @@ -308,6 +321,23 @@ describe('union', () => { }); }); + // regression test for https://github.com/streamich/unionfs/issues/782 + it('does not throw error when directory is empty and other fs fails', done => { + const vol = Volume.fromJSON({}); + const vol2 = Volume.fromJSON({ + '/bar': null, + }); + + const ufs = new Union(); + ufs.use(vol as any); + ufs.use(vol2 as any); + ufs.readdir('/bar', (err, files) => { + expect(err).toBeNull(); + expect(files).toEqual([]); + done(); + }); + }); + it('honors the withFileTypes: true option', done => { const vol = Volume.fromJSON({ '/foo/bar': 'bar', diff --git a/src/union.ts b/src/union.ts index 44e63237..d22571d0 100644 --- a/src/union.ts +++ b/src/union.ts @@ -176,18 +176,23 @@ export class Union { lastarg++; } + let numErrors = 0; let lastError: IUnionFsError | null = null; let result = new Map(); const iterate = (i = 0, error?: IUnionFsError | null) => { if (error) { error.prev = lastError; lastError = error; + numErrors++; } - // Already tried all file systems, return the last error. + // Already tried all file systems if (i >= this.fss.length) { - // last one if (cb) { + // If any previous file system succeeded, don't throw an error. + if (numErrors < this.fss.length) { + return cb(null, this.sortedArrayFromReaddirResult(result)); + } cb(error || Error('No file systems attached.')); } return; @@ -195,20 +200,13 @@ export class Union { // Replace `callback` with our intermediate function. args[lastarg] = (err, resArg: readdirEntry[]) => { - if (result.size === 0 && err) { - return iterate(i + 1, err); - } if (resArg) { for (const res of resArg) { result.set(this.pathFromReaddirEntry(res), res); } } - if (i === this.fss.length - 1) { - return cb(null, this.sortedArrayFromReaddirResult(result)); - } else { - return iterate(i + 1, error); - } + return iterate(i + 1, err); }; const j = this.fss.length - i - 1; @@ -222,6 +220,7 @@ export class Union { }; public readdirSync = (...args): Array => { + let numErrors = 0; let lastError: IUnionFsError | null = null; let result = new Map(); for (let i = this.fss.length - 1; i >= 0; i--) { @@ -234,8 +233,9 @@ export class Union { } catch (err) { err.prev = lastError; lastError = err; - if (result.size === 0 && !i) { - // last one + numErrors++; + if (numErrors === this.fss.length) { + // all filesystems errored throw err; } else { // Ignore error...